mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d98746501 | ||
|
|
63b03ba70c | ||
|
|
70bab76b36 | ||
|
|
15d24d4308 | ||
|
|
9ec62eb045 | ||
|
|
12f841e30d | ||
|
|
335599ed22 | ||
|
|
0b717f9e76 | ||
|
|
e941f6ecca | ||
|
|
ef7744dbda | ||
|
|
c83a61c460 | ||
|
|
335684caf7 | ||
|
|
8d6220ce51 | ||
|
|
39ea5c5f99 | ||
|
|
b03597ac13 | ||
|
|
58f323c087 | ||
|
|
513a68584c | ||
|
|
88d5c68b32 | ||
|
|
14f9382cf9 | ||
|
|
cffb582568 | ||
|
|
e1812ce16c | ||
|
|
7a3163f59a | ||
|
|
6f3b2749b0 | ||
|
|
c144d4e501 | ||
|
|
edfd9d55ba | ||
|
|
774897260e | ||
|
|
65ba91411d |
No files matched your search
@@ -201,6 +201,16 @@ pop_float_fac1 .proc
|
||||
jmp MOVFM
|
||||
.pend
|
||||
|
||||
pop_float_fac2 .proc
|
||||
; -- pops float from stack into FAC2
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jsr pop_float
|
||||
lda #<fmath_float1
|
||||
ldy #>fmath_float1
|
||||
jmp CONUPK
|
||||
.pend
|
||||
|
||||
pop_float_to_indexed_var .proc
|
||||
; -- pop the float on the stack, to the memory in the array at A/Y indexed by the byte on stack
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
|
||||
@@ -52,6 +52,7 @@ multiply_words .proc
|
||||
; -- multiply two 16-bit words into a 32-bit result (signed and unsigned)
|
||||
; input: A/Y = first 16-bit number, c64.SCRATCH_ZPWORD1 in ZP = second 16-bit number
|
||||
; output: multiply_words.result 4-bytes/32-bits product, LSB order (low-to-high)
|
||||
; clobbers: A
|
||||
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
|
||||
@@ -36,13 +36,26 @@ init_system .proc
|
||||
.pend
|
||||
|
||||
|
||||
read_byte_from_address .proc
|
||||
read_byte_from_address_on_stack .proc
|
||||
; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged)
|
||||
lda c64.ESTACK_LO+1,x
|
||||
ldy c64.ESTACK_HI+1,x
|
||||
sta (+) +1
|
||||
sty (+) +2
|
||||
+ lda $ffff ; modified
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy #0
|
||||
lda (c64.SCRATCH_ZPWORD2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
write_byte_to_address_on_stack .proc
|
||||
; -- write the byte in A to the memory address on the top of the stack (stack remains unchanged)
|
||||
ldy c64.ESTACK_LO+1,x
|
||||
sty c64.SCRATCH_ZPWORD2
|
||||
ldy c64.ESTACK_HI+1,x
|
||||
sty c64.SCRATCH_ZPWORD2+1
|
||||
ldy #0
|
||||
sta (c64.SCRATCH_ZPWORD2),y
|
||||
rts
|
||||
.pend
|
||||
|
||||
@@ -330,9 +343,7 @@ mul_word .proc
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
lda c64.ESTACK_LO+1,x
|
||||
ldy c64.ESTACK_HI+1,x
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
jsr math.multiply_words
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
lda math.multiply_words.result
|
||||
sta c64.ESTACK_LO+1,x
|
||||
lda math.multiply_words.result+1
|
||||
@@ -2079,7 +2090,7 @@ ror2_array_uw .proc
|
||||
+ rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
strcpy .proc
|
||||
; copy a string (0-terminated) from A/Y to (ZPWORD1)
|
||||
; it is assumed the target string is large enough.
|
||||
@@ -2092,8 +2103,8 @@ strcpy .proc
|
||||
bne -
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
|
||||
func_leftstr .proc
|
||||
; leftstr(source, target, length) with params on stack
|
||||
inx
|
||||
@@ -2175,12 +2186,12 @@ func_substr .proc
|
||||
lda c64.ESTACK_LO,x ; start
|
||||
sta c64.SCRATCH_ZPB1
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
lda c64.ESTACK_LO,x
|
||||
sta c64.SCRATCH_ZPWORD2
|
||||
lda c64.ESTACK_HI,x
|
||||
sta c64.SCRATCH_ZPWORD2+1
|
||||
inx
|
||||
lda c64.ESTACK_LO,x
|
||||
lda c64.ESTACK_LO,x
|
||||
sta c64.SCRATCH_ZPWORD1
|
||||
lda c64.ESTACK_HI,x
|
||||
sta c64.SCRATCH_ZPWORD1+1
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.1
|
||||
3.2
|
||||
@@ -314,9 +314,9 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||
}
|
||||
// lookup something from the module.
|
||||
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||
is Label, is VarDecl, is Block, is Subroutine, is StructDecl -> stmt
|
||||
null -> null
|
||||
else -> throw SyntaxError("wrong identifier target for $scopedName: $stmt", stmt.position)
|
||||
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,7 +167,8 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
||||
augassignment()?.let {
|
||||
// replace A += X with A = A + X
|
||||
val target = it.assign_target().toAst()
|
||||
val expression = BinaryExpression(target.toExpression(), it.operator.text.substring(0, 1), it.expression().toAst(), it.expression().toPosition())
|
||||
val oper = it.operator.text.substringBefore('=')
|
||||
val expression = BinaryExpression(target.toExpression(), oper, it.expression().toAst(), it.expression().toPosition())
|
||||
return Assignment(it.assign_target().toAst(), expression, it.toPosition())
|
||||
}
|
||||
|
||||
@@ -301,7 +302,7 @@ private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParamete
|
||||
when (val name = register.nameInSource.single()) {
|
||||
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(name)
|
||||
in Statusflag.names -> statusregister = Statusflag.valueOf(name)
|
||||
else -> throw FatalAstException("invalid register or status flag in $it")
|
||||
else -> throw FatalAstException("invalid register or status flag '$name'")
|
||||
}
|
||||
}
|
||||
AsmSubroutineParameter(vardecl.varname.text, datatype, registerorpair, statusregister,
|
||||
|
||||
@@ -58,7 +58,7 @@ enum class DataType {
|
||||
in ByteDatatypes -> 1
|
||||
in WordDatatypes -> 2
|
||||
FLOAT -> CompilationTarget.machine.FLOAT_MEM_SIZE
|
||||
in PassByReferenceDatatypes -> 2
|
||||
in PassByReferenceDatatypes -> CompilationTarget.machine.POINTER_MEM_SIZE
|
||||
else -> -9999999
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,23 @@ sealed class Expression: Node {
|
||||
(other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
(other is DirectMemoryRead && other.addressExpression isSameAs addressExpression)
|
||||
}
|
||||
is TypecastExpression -> {
|
||||
(other is TypecastExpression && other.implicit==implicit && other.type==type && other.expression isSameAs expression)
|
||||
}
|
||||
is AddressOf -> {
|
||||
(other is AddressOf && other.identifier.nameInSource == identifier.nameInSource)
|
||||
}
|
||||
is RangeExpr -> {
|
||||
(other is RangeExpr && other.from==from && other.to==to && other.step==step)
|
||||
}
|
||||
is FunctionCall -> {
|
||||
(other is FunctionCall && other.target.nameInSource == target.nameInSource
|
||||
&& other.args.size == args.size
|
||||
&& other.args.zip(args).all { it.first isSameAs it.second } )
|
||||
}
|
||||
else -> other==this
|
||||
}
|
||||
}
|
||||
@@ -277,7 +294,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||
override fun constValue(program: Program): NumericLiteralValue? {
|
||||
val cv = expression.constValue(program) ?: return null
|
||||
return cv.cast(type)
|
||||
return cv.castNoCheck(type)
|
||||
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
||||
// return LiteralValue.fromNumber(value.numericValue(), value.type, position).cast(type)
|
||||
}
|
||||
@@ -398,7 +415,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||
|
||||
operator fun compareTo(other: NumericLiteralValue): Int = number.toDouble().compareTo(other.number.toDouble())
|
||||
|
||||
fun cast(targettype: DataType): NumericLiteralValue {
|
||||
fun castNoCheck(targettype: DataType): NumericLiteralValue {
|
||||
if(type==targettype)
|
||||
return this
|
||||
val numval = number.toDouble()
|
||||
@@ -567,7 +584,7 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
||||
it
|
||||
} else {
|
||||
try {
|
||||
num.cast(elementType)
|
||||
num.castNoCheck(elementType)
|
||||
} catch(x: ExpressionError) {
|
||||
return null
|
||||
}
|
||||
@@ -716,11 +733,10 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name
|
||||
|
||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||
val targetStmt = targetStatement(program.namespace)
|
||||
return if(targetStmt is VarDecl) {
|
||||
InferredTypes.knownFor(targetStmt.datatype)
|
||||
} else {
|
||||
InferredTypes.InferredType.unknown()
|
||||
return when (val targetStmt = targetStatement(program.namespace)) {
|
||||
is VarDecl -> InferredTypes.knownFor(targetStmt.datatype)
|
||||
is StructDecl -> InferredTypes.knownFor(DataType.STRUCT)
|
||||
else -> InferredTypes.InferredType.unknown()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -304,13 +304,11 @@ internal class AstChecker(private val program: Program,
|
||||
} else {
|
||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||
// Instead, their reference (address) should be passed (as an UWORD).
|
||||
// TODO The language has no typed pointers at this time! This should be handled better.
|
||||
// TODO The language has no (typed) pointers at this time. Should this be handled better?
|
||||
if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) {
|
||||
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword to receive their address, or access the variable from the outer scope directly.")
|
||||
}
|
||||
}
|
||||
|
||||
visitStatements(subroutine.statements)
|
||||
}
|
||||
|
||||
override fun visit(untilLoop: UntilLoop) {
|
||||
@@ -362,9 +360,15 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(assignment.value.inferType(program) != assignment.target.inferType(program, assignment))
|
||||
val targetDt = assignment.target.inferType(program, assignment)
|
||||
if(assignment.value.inferType(program) != targetDt)
|
||||
errors.err("assignment value is of different type as the target", assignment.value.position)
|
||||
|
||||
if(assignment.value is TypecastExpression) {
|
||||
if(assignment.isAugmentable && targetDt.istype(DataType.FLOAT))
|
||||
errors.err("typecasting a float value in-place makes no sense", assignment.value.position)
|
||||
}
|
||||
|
||||
super.visit(assignment)
|
||||
}
|
||||
|
||||
@@ -434,7 +438,9 @@ internal class AstChecker(private val program: Program,
|
||||
if(variable==null)
|
||||
errors.err("pointer-of operand must be the name of a heap variable", addressOf.position)
|
||||
else {
|
||||
if(variable.datatype !in ArrayDatatypes && variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||
if(variable.datatype !in ArrayDatatypes
|
||||
&& variable.type!=VarDeclType.MEMORY
|
||||
&& variable.datatype != DataType.STR && variable.datatype!=DataType.STRUCT)
|
||||
errors.err("invalid pointer-of operand type", addressOf.position)
|
||||
}
|
||||
super.visit(addressOf)
|
||||
@@ -710,12 +716,20 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(expr: PrefixExpression) {
|
||||
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if(expr.operator=="-") {
|
||||
val dt = expr.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||
errors.err("can only take negative of a signed number type", expr.position)
|
||||
}
|
||||
}
|
||||
else if(expr.operator == "not") {
|
||||
if(dt !in IntegerDatatypes)
|
||||
errors.err("can only use boolean not on integer types", expr.position)
|
||||
}
|
||||
else if(expr.operator == "~") {
|
||||
if(dt !in IntegerDatatypes)
|
||||
errors.err("can only use bitwise invert on integer types", expr.position)
|
||||
}
|
||||
super.visit(expr)
|
||||
}
|
||||
|
||||
@@ -758,7 +772,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("bitwise operator can only be used on integer operands", expr.right.position)
|
||||
}
|
||||
"<<", ">>" -> {
|
||||
// for now, bit-shifts can only shift by a constant number
|
||||
// for now, bit-shifts can only shift by a constant number TODO remove this restriction
|
||||
val constRight = expr.right.constValue(program)
|
||||
if(constRight==null)
|
||||
errors.err("bit-shift can only be done by a constant number (for now)", expr.right.position)
|
||||
@@ -863,8 +877,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, functionCallStatement.definingScope(), program)
|
||||
if(error!=null)
|
||||
errors.err(error, functionCallStatement.args.first().position)
|
||||
if(error!=null) {
|
||||
errors.err(error, functionCallStatement.args.firstOrNull()?.position ?: functionCallStatement.position)
|
||||
}
|
||||
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
@@ -874,79 +889,38 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("cannot use arguments when calling a label", position)
|
||||
|
||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||
// it's a call to a builtin function.
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
if(args.size!=func.parameters.size)
|
||||
errors.err("invalid number of arguments", position)
|
||||
else {
|
||||
val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
||||
for (arg in args.withIndex().zip(func.parameters)) {
|
||||
val argDt=arg.first.value.inferType(program)
|
||||
if (argDt.isKnown
|
||||
&& !(argDt.typeOrElse(DataType.STRUCT) isAssignableTo arg.second.possibleDatatypes)
|
||||
&& (argDt.typeOrElse(DataType.STRUCT) != DataType.UWORD || arg.second.possibleDatatypes.intersect(paramTypesForAddressOf).isEmpty())) {
|
||||
errors.err("builtin function '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.possibleDatatypes}", position)
|
||||
}
|
||||
if(target.name=="swap") {
|
||||
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
||||
val dt1 = args[0].inferType(program)
|
||||
val dt2 = args[1].inferType(program)
|
||||
if (dt1 != dt2)
|
||||
errors.err("swap requires 2 args of identical type", position)
|
||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
||||
else if(args[0] isSameAs args[1])
|
||||
errors.err("swap should have 2 different args", position)
|
||||
else if(dt1.typeOrElse(DataType.STRUCT) !in NumericDatatypes)
|
||||
errors.err("swap requires args of numerical type", position)
|
||||
}
|
||||
else if(target.name=="all" || target.name=="any") {
|
||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
if(target.name=="swap") {
|
||||
// swap() is a bit weird because this one is translated into a operations directly, instead of being a function call
|
||||
val dt1 = args[0].inferType(program)
|
||||
val dt2 = args[1].inferType(program)
|
||||
if (dt1 != dt2)
|
||||
errors.err("swap requires 2 args of identical type", position)
|
||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
||||
else if(args[0] isSameAs args[1])
|
||||
errors.err("swap should have 2 different args", position)
|
||||
else if(dt1.typeOrElse(DataType.STRUCT) !in NumericDatatypes)
|
||||
errors.err("swap requires args of numerical type", position)
|
||||
}
|
||||
else if(target.name=="all" || target.name=="any") {
|
||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program.namespace)?.datatype == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
if(args[0].inferType(program).typeOrElse(DataType.STR) == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
if(args[0].inferType(program).typeOrElse(DataType.STR) == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
}
|
||||
}
|
||||
} else if(target is Subroutine) {
|
||||
if(target.regXasResult())
|
||||
errors.warn("subroutine call return value in X register is discarded and replaced by 0", position)
|
||||
if(args.size!=target.parameters.size)
|
||||
errors.err("invalid number of arguments", position)
|
||||
else {
|
||||
if(target.isAsmSubroutine) {
|
||||
for (arg in args.withIndex().zip(target.parameters)) {
|
||||
val argIDt = arg.first.value.inferType(program)
|
||||
if(!argIDt.isKnown) {
|
||||
if (!argIDt.isKnown)
|
||||
return
|
||||
}
|
||||
val argDt=argIDt.typeOrElse(DataType.STRUCT)
|
||||
if(!(argDt isAssignableTo arg.second.type)) {
|
||||
// for asm subroutines having STR param it's okay to provide a UWORD (address value)
|
||||
if(!(target.isAsmSubroutine && arg.second.type == DataType.STR && argDt == DataType.UWORD))
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position)
|
||||
}
|
||||
|
||||
if(target.isAsmSubroutine) {
|
||||
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)) {
|
||||
if (arg.first.value !is NumericLiteralValue && arg.first.value !is IdentifierReference)
|
||||
errors.warn("calling a subroutine that expects X as a parameter is problematic. If you see a compiler error/crash about this later, try to change this call", position)
|
||||
}
|
||||
|
||||
// check if the argument types match the register(pairs)
|
||||
val asmParamReg = target.asmParameterRegisters[arg.first.index]
|
||||
if(asmParamReg.statusflag!=null) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for statusflag", position)
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for single register", position)
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||
if(argDt !in WordDatatypes + IterableDatatypes)
|
||||
errors.err("subroutine '${target.name}' argument ${arg.first.index + 1} must be word type for register pair", position)
|
||||
}
|
||||
}
|
||||
if (target.asmParameterRegisters[arg.first.index].registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.XY, RegisterOrPair.X)
|
||||
&& arg.first.value !is NumericLiteralValue && arg.first.value !is IdentifierReference)
|
||||
errors.warn("calling a subroutine that expects X as a parameter is problematic. If you see a compiler error/crash about this later, try to change this call", position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1067,30 +1041,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(scope: AnonymousScope) {
|
||||
visitStatements(scope.statements)
|
||||
}
|
||||
|
||||
private fun visitStatements(statements: List<Statement>) {
|
||||
for((index, stmt) in statements.withIndex()) {
|
||||
if(index < statements.lastIndex && statements[index+1] !is Subroutine) {
|
||||
when {
|
||||
stmt is FunctionCallStatement && stmt.target.nameInSource.last() == "exit" -> {
|
||||
errors.warn("unreachable code, preceding exit call will never return", statements[index + 1].position)
|
||||
}
|
||||
stmt is Return && statements[index + 1] !is Subroutine -> {
|
||||
errors.warn("unreachable code, preceding return statement", statements[index + 1].position)
|
||||
}
|
||||
stmt is Jump && statements[index + 1] !is Subroutine -> {
|
||||
errors.warn("unreachable code, preceding jump statement", statements[index + 1].position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmt.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||
val targetStatement = target.targetStatement(program.namespace)
|
||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||
@@ -1265,7 +1215,7 @@ internal class AstChecker(private val program: Program,
|
||||
is AddressOf -> it.identifier.heapId(program.namespace)
|
||||
is TypecastExpression -> {
|
||||
val constVal = it.expression.constValue(program)
|
||||
constVal?.cast(it.type)?.number?.toInt() ?: -9999999
|
||||
constVal?.castNoCheck(it.type)?.number?.toInt() ?: -9999999
|
||||
}
|
||||
else -> -9999999
|
||||
}
|
||||
|
||||
@@ -47,18 +47,6 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
||||
val typecast = TypecastExpression(functionCall.args.single(), DataType.UBYTE, false, functionCall.position)
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
functionCall, typecast, parent
|
||||
))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
// is it a struct variable? then define all its struct members as mangled names,
|
||||
// and include the original decl as well.
|
||||
|
||||
@@ -52,7 +52,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
assignment))
|
||||
} else {
|
||||
fun castLiteral(cvalue: NumericLiteralValue): List<IAstModification.ReplaceNode> =
|
||||
listOf(IAstModification.ReplaceNode(cvalue, cvalue.cast(targettype), cvalue.parent))
|
||||
listOf(IAstModification.ReplaceNode(cvalue, cvalue.castNoCheck(targettype), cvalue.parent))
|
||||
val cvalue = assignment.value.constValue(program)
|
||||
if(cvalue!=null) {
|
||||
val number = cvalue.number.toDouble()
|
||||
@@ -109,14 +109,16 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
AddressOf(arg.second.value as IdentifierReference, arg.second.value.position),
|
||||
call as Node)
|
||||
} else if(arg.second.value is NumericLiteralValue) {
|
||||
try {
|
||||
val castedValue = (arg.second.value as NumericLiteralValue).cast(requiredType)
|
||||
modifications += IAstModification.ReplaceNode(
|
||||
call.args[arg.second.index],
|
||||
castedValue,
|
||||
call as Node)
|
||||
} catch (x: ExpressionError) {
|
||||
// no cast possible
|
||||
if(argtype.isAssignableTo(requiredType)) {
|
||||
try {
|
||||
val castedValue = (arg.second.value as NumericLiteralValue).castNoCheck(requiredType)
|
||||
modifications += IAstModification.ReplaceNode(
|
||||
call.args[arg.second.index],
|
||||
castedValue,
|
||||
call as Node)
|
||||
} catch (x: ExpressionError) {
|
||||
// cast failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,7 +163,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
// make sure the memory address is an uword
|
||||
val dt = memread.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||
val typecast = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
||||
val typecast = (memread.addressExpression as? NumericLiteralValue)?.castNoCheck(DataType.UWORD)
|
||||
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
||||
}
|
||||
@@ -172,7 +174,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
// make sure the memory address is an uword
|
||||
val dt = memwrite.addressExpression.inferType(program)
|
||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
||||
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.castNoCheck(DataType.UWORD)
|
||||
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
||||
}
|
||||
@@ -189,7 +191,7 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
if (returnValue.inferType(program).istype(subReturnType))
|
||||
return noModifications
|
||||
if (returnValue is NumericLiteralValue) {
|
||||
returnStmt.value = returnValue.cast(subroutine.returntypes.single())
|
||||
returnStmt.value = returnValue.castNoCheck(subroutine.returntypes.single())
|
||||
} else {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
returnValue,
|
||||
|
||||
@@ -34,7 +34,7 @@ internal class VariousCleanups: AstWalker() {
|
||||
|
||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(typecast.expression is NumericLiteralValue) {
|
||||
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
||||
val value = (typecast.expression as NumericLiteralValue).castNoCheck(typecast.type)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value, parent))
|
||||
}
|
||||
|
||||
|
||||
@@ -29,24 +29,32 @@ class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||
fun checkTypes(call: IFunctionCall, scope: INameScope, program: Program): String? {
|
||||
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||
val target = call.target.targetStatement(scope)
|
||||
when (target) {
|
||||
is Subroutine -> {
|
||||
val paramtypes = target.parameters.map { it.type }
|
||||
val mismatch = argtypes.zip(paramtypes).indexOfFirst { it.first != it.second}
|
||||
if(mismatch>=0)
|
||||
return "argument ${mismatch+1} type mismatch"
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||
for (x in argtypes.zip(paramtypes).withIndex()) {
|
||||
if (x.value.first !in x.value.second)
|
||||
return "argument ${x.index+1} type mismatch"
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if (target is Subroutine) {
|
||||
// asmsub types are not checked specifically at this time
|
||||
if(call.args.size != target.parameters.size)
|
||||
return "invalid number of arguments"
|
||||
val paramtypes = target.parameters.map { it.type }
|
||||
val mismatch = argtypes.zip(paramtypes).indexOfFirst { it.first != it.second}
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch].toString()
|
||||
val expected = paramtypes[mismatch].toString()
|
||||
return "argument ${mismatch + 1} type mismatch, was: $actual expected: $expected"
|
||||
}
|
||||
}
|
||||
else if (target is BuiltinFunctionStatementPlaceholder) {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
if(call.args.size != func.parameters.size)
|
||||
return "invalid number of arguments"
|
||||
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||
for (x in argtypes.zip(paramtypes).withIndex()) {
|
||||
if (x.value.first !in x.value.second) {
|
||||
val actual = x.value.first.toString()
|
||||
val expected = x.value.second.toString()
|
||||
return "argument ${x.index + 1} type mismatch, was: $actual expected: $expected"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,15 +139,6 @@ open class Return(var value: Expression?, override val position: Position) : Sta
|
||||
}
|
||||
}
|
||||
|
||||
class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "ReturnFromIrq(pos=$position)"
|
||||
}
|
||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||
}
|
||||
|
||||
class Break(override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
@@ -337,32 +328,50 @@ open class Assignment(var target: AssignTarget, var value: Expression, override
|
||||
return("Assignment(target: $target, value: $value, pos=$position)")
|
||||
}
|
||||
|
||||
val isInplace: Boolean
|
||||
/**
|
||||
* Is the assigment value an expression that references the assignment target itself?
|
||||
* The expression can be a BinaryExpression, PrefixExpression or TypecastExpression (possibly with one sub-cast).
|
||||
*/
|
||||
val isAugmentable: Boolean
|
||||
get() {
|
||||
val binExpr = value as? BinaryExpression
|
||||
if(binExpr!=null) {
|
||||
if(binExpr.left isSameAs target)
|
||||
return true // A = A <operator> 5
|
||||
if(binExpr.right !is BinaryExpression && binExpr.left isSameAs target)
|
||||
return true // A = A <operator> v
|
||||
|
||||
if(binExpr.operator in associativeOperators) {
|
||||
if (binExpr.right isSameAs target)
|
||||
if (binExpr.left !is BinaryExpression && binExpr.right isSameAs target)
|
||||
return true // A = v <associative-operator> A
|
||||
|
||||
val leftBinExpr = binExpr.left as? BinaryExpression
|
||||
if(leftBinExpr?.operator == binExpr.operator) {
|
||||
// one of these?
|
||||
// A = (A <associative-operator> x) <same-operator> y
|
||||
// A = (x <associative-operator> A) <same-operator> y
|
||||
return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target
|
||||
// A = (x <associative-operator> y) <same-operator> A
|
||||
return leftBinExpr.left isSameAs target || leftBinExpr.right isSameAs target || binExpr.right isSameAs target
|
||||
}
|
||||
val rightBinExpr = binExpr.right as? BinaryExpression
|
||||
if(rightBinExpr?.operator == binExpr.operator) {
|
||||
// one of these?
|
||||
// A = y <associative-operator> (A <same-operator> x)
|
||||
// A = y <associative-operator> (x <same-operator> y)
|
||||
return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target
|
||||
// A = A <associative-operator> (x <same-operator> y)
|
||||
return rightBinExpr.left isSameAs target || rightBinExpr.right isSameAs target || binExpr.left isSameAs target
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val prefixExpr = value as? PrefixExpression
|
||||
if(prefixExpr!=null)
|
||||
return prefixExpr.expression isSameAs target
|
||||
|
||||
val castExpr = value as? TypecastExpression
|
||||
if(castExpr!=null) {
|
||||
val subCast = castExpr.expression as? TypecastExpression
|
||||
return if(subCast!=null) subCast.expression isSameAs target else castExpr.expression isSameAs target
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -624,7 +633,6 @@ class Subroutine(override val name: String,
|
||||
override var statements: MutableList<Statement>,
|
||||
override val position: Position) : Statement(), INameScope {
|
||||
|
||||
var keepAlways: Boolean = false
|
||||
override lateinit var parent: Node
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
|
||||
|
||||
@@ -89,7 +89,14 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: E
|
||||
return listOf(IAstModification.ReplaceNode(typecast, typecast.expression, parent))
|
||||
}
|
||||
}
|
||||
else if(sourceDt in PassByReferenceDatatypes) {
|
||||
|
||||
|
||||
// Note: for various reasons (most importantly, code simplicity), the code generator assumes/requires
|
||||
// that the types of assignment values and their target are the same,
|
||||
// and that the types of both operands of a binaryexpression node are the same.
|
||||
// So, it is not easily possible to remove the typecasts that are there to make these conditions true.
|
||||
|
||||
if(sourceDt in PassByReferenceDatatypes) {
|
||||
if(typecast.type==DataType.UWORD) {
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
typecast,
|
||||
|
||||
@@ -169,9 +169,10 @@ private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
|
||||
break
|
||||
}
|
||||
|
||||
val remover = UnusedCodeRemover()
|
||||
val remover = UnusedCodeRemover(errors)
|
||||
remover.visit(programAst)
|
||||
remover.applyModifications()
|
||||
errors.handle()
|
||||
}
|
||||
|
||||
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
||||
|
||||
@@ -8,6 +8,7 @@ interface IMachineDefinition {
|
||||
val FLOAT_MAX_NEGATIVE: Double
|
||||
val FLOAT_MAX_POSITIVE: Double
|
||||
val FLOAT_MEM_SIZE: Int
|
||||
val POINTER_MEM_SIZE: Int
|
||||
|
||||
val opcodeNames: Set<String>
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ class AssemblyProgram(override val name: String, outputDir: Path) : IAssemblyPro
|
||||
private val viceMonListFile = outputDir.resolve("$name.vice-mon-list")
|
||||
|
||||
override fun assemble(options: CompilationOptions) {
|
||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
|
||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",
|
||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
||||
|
||||
val outFile = when (options.output) {
|
||||
|
||||
@@ -14,6 +14,7 @@ object C64MachineDefinition: IMachineDefinition {
|
||||
override val FLOAT_MAX_POSITIVE = 1.7014118345e+38 // bytes: 255,127,255,255,255
|
||||
override val FLOAT_MAX_NEGATIVE = -1.7014118345e+38 // bytes: 255,255,255,255,255
|
||||
override val FLOAT_MEM_SIZE = 5
|
||||
override val POINTER_MEM_SIZE = 2
|
||||
const val BASIC_LOAD_ADDRESS = 0x0801
|
||||
const val RAW_LOAD_ADDRESS = 0xc000
|
||||
|
||||
|
||||
@@ -636,7 +636,11 @@ internal class AsmGen(private val program: Program,
|
||||
is BranchStatement -> translate(stmt)
|
||||
is IfStatement -> translate(stmt)
|
||||
is ForLoop -> forloopsAsmGen.translate(stmt)
|
||||
is Break -> out(" jmp ${loopEndLabels.peek()}")
|
||||
is Break -> {
|
||||
if(loopEndLabels.isEmpty())
|
||||
throw AssemblyError("break statement out of context ${stmt.position}")
|
||||
out(" jmp ${loopEndLabels.peek()}")
|
||||
}
|
||||
is WhileLoop -> translate(stmt)
|
||||
is RepeatLoop -> translate(stmt)
|
||||
is UntilLoop -> translate(stmt)
|
||||
@@ -1004,7 +1008,6 @@ $counterVar .byte 0""")
|
||||
val indexName = asmIdentifierName(index)
|
||||
out(" lda $indexName")
|
||||
}
|
||||
// TODO optimize more cases
|
||||
else -> {
|
||||
expressionsAsmGen.translateExpression(index)
|
||||
out(" inx | lda $ESTACK_LO_HEX,x")
|
||||
@@ -1021,36 +1024,6 @@ $counterVar .byte 0""")
|
||||
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCall)
|
||||
|
||||
internal fun assignFromEvalResult(target: AssignTarget) =
|
||||
assignmentAsmGen.assignFromEvalResult(target)
|
||||
|
||||
fun assignFromByteConstant(target: AssignTarget, value: Short) =
|
||||
assignmentAsmGen.assignFromByteConstant(target, value)
|
||||
|
||||
fun assignFromWordConstant(target: AssignTarget, value: Int) =
|
||||
assignmentAsmGen.assignFromWordConstant(target, value)
|
||||
|
||||
fun assignFromFloatConstant(target: AssignTarget, value: Double) =
|
||||
assignmentAsmGen.assignFromFloatConstant(target, value)
|
||||
|
||||
fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) =
|
||||
assignmentAsmGen.assignFromByteVariable(target, variable)
|
||||
|
||||
fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) =
|
||||
assignmentAsmGen.assignFromWordVariable(target, variable)
|
||||
|
||||
fun assignFromAddressOf(target: AssignTarget, variable: IdentifierReference) =
|
||||
assignmentAsmGen.assignFromAddressOf(target, variable)
|
||||
|
||||
fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) =
|
||||
assignmentAsmGen.assignFromFloatVariable(target, variable)
|
||||
|
||||
fun assignFromRegister(target: AssignTarget, register: CpuRegister) =
|
||||
assignmentAsmGen.assignFromRegister(target, register)
|
||||
|
||||
fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) =
|
||||
assignmentAsmGen.assignFromMemoryByte(target, address, identifier)
|
||||
|
||||
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
|
||||
internal fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) =
|
||||
assignmentAsmGen.assignToRegister(reg, value, identifier)
|
||||
}
|
||||
@@ -3,10 +3,7 @@ package prog8.compiler.target.c64.codegen
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||
@@ -17,34 +14,49 @@ import prog8.compiler.toHex
|
||||
|
||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||
|
||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen)
|
||||
|
||||
internal fun translate(assign: Assignment) {
|
||||
if(assign.isInplace)
|
||||
translateNormalAssignment(assign) // TODO generate better code here for in-place assignments
|
||||
else
|
||||
translateNormalAssignment(assign)
|
||||
when {
|
||||
assign.value is NumericLiteralValue -> translateConstantValueAssignment(assign)
|
||||
assign.value is IdentifierReference -> translateVariableAssignment(assign)
|
||||
assign.isAugmentable -> augmentableAsmGen.translate(assign)
|
||||
else -> translateOtherAssignment(assign)
|
||||
}
|
||||
}
|
||||
|
||||
// old code-generation below:
|
||||
// eventually, all of this should have been replaced by newer more optimized code.
|
||||
private fun translateNormalAssignment(assign: Assignment) {
|
||||
internal fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) {
|
||||
if(value!=null) {
|
||||
asmgen.out(" ld${reg.toString().toLowerCase()} #${value.toHex()}")
|
||||
} else if(identifier!=null) {
|
||||
val name = asmgen.asmIdentifierName(identifier)
|
||||
asmgen.out(" ld${reg.toString().toLowerCase()} $name")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateVariableAssignment(assign: Assignment) {
|
||||
val identifier = assign.value as IdentifierReference
|
||||
when (val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)) {
|
||||
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, identifier)
|
||||
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, identifier)
|
||||
DataType.FLOAT -> assignFromFloatVariable(assign.target, identifier)
|
||||
in PassByReferenceDatatypes -> assignFromAddressOf(assign.target, identifier)
|
||||
else -> throw AssemblyError("unsupported assignment target type $type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateConstantValueAssignment(assign: Assignment) {
|
||||
val numVal = assign.value as NumericLiteralValue
|
||||
when (numVal.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> assignFromByteConstant(assign.target, numVal.number.toShort())
|
||||
DataType.UWORD, DataType.WORD -> assignFromWordConstant(assign.target, numVal.number.toInt())
|
||||
DataType.FLOAT -> assignFromFloatConstant(assign.target, numVal.number.toDouble())
|
||||
else -> throw AssemblyError("weird numval type")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translateOtherAssignment(assign: Assignment) {
|
||||
when (assign.value) {
|
||||
is NumericLiteralValue -> {
|
||||
val numVal = assign.value as NumericLiteralValue
|
||||
when (numVal.type) {
|
||||
DataType.UBYTE, DataType.BYTE -> assignFromByteConstant(assign.target, numVal.number.toShort())
|
||||
DataType.UWORD, DataType.WORD -> assignFromWordConstant(assign.target, numVal.number.toInt())
|
||||
DataType.FLOAT -> assignFromFloatConstant(assign.target, numVal.number.toDouble())
|
||||
else -> throw AssemblyError("weird numval type")
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
when (val type = assign.target.inferType(program, assign).typeOrElse(DataType.STRUCT)) {
|
||||
DataType.UBYTE, DataType.BYTE -> assignFromByteVariable(assign.target, assign.value as IdentifierReference)
|
||||
DataType.UWORD, DataType.WORD -> assignFromWordVariable(assign.target, assign.value as IdentifierReference)
|
||||
DataType.FLOAT -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference)
|
||||
else -> throw AssemblyError("unsupported assignment target type $type")
|
||||
}
|
||||
}
|
||||
is AddressOf -> {
|
||||
val identifier = (assign.value as AddressOf).identifier
|
||||
assignFromAddressOf(assign.target, identifier)
|
||||
@@ -60,22 +72,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference)
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("missing asm gen for memread assignment into ${assign.target}")
|
||||
asmgen.translateExpression(read.addressExpression)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | inx")
|
||||
assignFromRegister(assign.target, CpuRegister.A)
|
||||
}
|
||||
}
|
||||
}
|
||||
is PrefixExpression -> {
|
||||
// TODO optimize common cases
|
||||
asmgen.translateExpression(assign.value as PrefixExpression)
|
||||
assignFromEvalResult(assign.target)
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
// TODO optimize common cases
|
||||
asmgen.translateExpression(assign.value as BinaryExpression)
|
||||
assignFromEvalResult(assign.target)
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
// TODO optimize common cases
|
||||
val arrayExpr = assign.value as ArrayIndexedExpression
|
||||
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val index = arrayExpr.arrayspec.index
|
||||
@@ -102,10 +113,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
is TypecastExpression -> {
|
||||
val cast = assign.value as TypecastExpression
|
||||
val sourceType = cast.expression.inferType(program)
|
||||
val targetType = assign.target.inferType(program, assign)
|
||||
if (sourceType.isKnown && targetType.isKnown &&
|
||||
(sourceType.typeOrElse(DataType.STRUCT) in ByteDatatypes && targetType.typeOrElse(DataType.STRUCT) in ByteDatatypes) ||
|
||||
(sourceType.typeOrElse(DataType.STRUCT) in WordDatatypes && targetType.typeOrElse(DataType.STRUCT) in WordDatatypes)) {
|
||||
if (sourceType.isKnown &&
|
||||
(sourceType.typeOrElse(DataType.STRUCT) in ByteDatatypes && cast.type in ByteDatatypes) ||
|
||||
(sourceType.typeOrElse(DataType.STRUCT) in WordDatatypes && cast.type in WordDatatypes)) {
|
||||
// no need for a type cast
|
||||
assign.value = cast.expression
|
||||
translate(assign)
|
||||
@@ -120,11 +130,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment $assign")
|
||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values ${assign.value.position}")
|
||||
else -> throw AssemblyError("assignment value type ${assign.value} should have been handled elsewhere")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromEvalResult(target: AssignTarget) {
|
||||
private fun assignFromEvalResult(target: AssignTarget) {
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
val targetMemory = target.memoryAddress
|
||||
when {
|
||||
targetIdent != null -> {
|
||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||
@@ -151,14 +164,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
else -> throw AssemblyError("weird target variable type $targetDt")
|
||||
}
|
||||
}
|
||||
target.memoryAddress != null -> {
|
||||
asmgen.out(" inx | ldy $ESTACK_LO_HEX,x")
|
||||
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
|
||||
targetMemory != null -> {
|
||||
asmgen.out(" inx")
|
||||
storeByteViaRegisterAInMemoryAddress("$ESTACK_LO_HEX,x", targetMemory)
|
||||
}
|
||||
target.arrayindexed != null -> {
|
||||
val arrayDt = target.arrayindexed!!.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val arrayVarName = asmgen.asmIdentifierName(target.arrayindexed!!.identifier)
|
||||
asmgen.translateExpression(target.arrayindexed!!.arrayspec.index)
|
||||
targetArrayIdx != null -> {
|
||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
val arrayVarName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||
asmgen.translateExpression(targetArrayIdx.arrayspec.index)
|
||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||
popAndWriteArrayvalueWithIndexA(arrayDt, arrayVarName)
|
||||
}
|
||||
@@ -166,7 +179,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromAddressOf(target: AssignTarget, name: IdentifierReference) {
|
||||
private fun assignFromAddressOf(target: AssignTarget, name: IdentifierReference) {
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
val struct = name.memberOfStruct(program.namespace)
|
||||
@@ -204,7 +217,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||
private fun assignFromWordVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||
val sourceName = asmgen.asmIdentifierName(variable)
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
@@ -234,7 +247,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||
private fun assignFromFloatVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||
val sourceName = asmgen.asmIdentifierName(variable)
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
@@ -265,10 +278,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||
private fun assignFromByteVariable(target: AssignTarget, variable: IdentifierReference) {
|
||||
val sourceName = asmgen.asmIdentifierName(variable)
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
val targetMemory = target.memoryAddress
|
||||
when {
|
||||
targetIdent != null -> {
|
||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||
@@ -277,6 +291,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
sta $targetName
|
||||
""")
|
||||
}
|
||||
targetMemory != null -> {
|
||||
storeByteViaRegisterAInMemoryAddress(sourceName, targetMemory)
|
||||
}
|
||||
targetArrayIdx != null -> {
|
||||
val index = targetArrayIdx.arrayspec.index
|
||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||
@@ -286,34 +303,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x")
|
||||
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||
}
|
||||
target.memoryAddress != null -> {
|
||||
val addressExpr = target.memoryAddress.addressExpression
|
||||
val addressLv = addressExpr as? NumericLiteralValue
|
||||
when {
|
||||
addressLv != null -> asmgen.out(" lda $sourceName | sta ${addressLv.number.toHex()}")
|
||||
addressExpr is IdentifierReference -> {
|
||||
val targetName = asmgen.asmIdentifierName(addressExpr)
|
||||
asmgen.out(" lda $sourceName | sta $targetName")
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateExpression(addressExpr)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
ldy $ESTACK_HI_HEX,x
|
||||
sta (+) +1
|
||||
sty (+) +2
|
||||
lda $sourceName
|
||||
+ sta ${'$'}ffff ; modified
|
||||
""")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("no asm gen for assign bytevar to $target")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromRegister(target: AssignTarget, register: CpuRegister) {
|
||||
private fun assignFromRegister(target: AssignTarget, register: CpuRegister) {
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
when {
|
||||
@@ -372,57 +366,80 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
|
||||
private fun storeByteViaRegisterAInMemoryAddress(ldaInstructionArg: String, memoryAddress: DirectMemoryWrite) {
|
||||
val addressExpr = memoryAddress.addressExpression
|
||||
val addressLv = addressExpr as? NumericLiteralValue
|
||||
val registerName = register.name.toLowerCase()
|
||||
when {
|
||||
addressLv != null -> asmgen.out(" st$registerName ${addressLv.number.toHex()}")
|
||||
addressLv != null -> {
|
||||
asmgen.out(" lda $ldaInstructionArg | sta ${addressLv.number.toHex()}")
|
||||
}
|
||||
addressExpr is IdentifierReference -> {
|
||||
val targetName = asmgen.asmIdentifierName(addressExpr)
|
||||
when (register) {
|
||||
CpuRegister.A -> asmgen.out("""
|
||||
ldy $targetName
|
||||
sty (+) +1
|
||||
ldy $targetName+1
|
||||
sty (+) +2
|
||||
+ sta ${'$'}ffff ; modified""")
|
||||
CpuRegister.X -> asmgen.out("""
|
||||
ldy $targetName
|
||||
sty (+) +1
|
||||
ldy $targetName+1
|
||||
sty (+) +2
|
||||
+ stx ${'$'}ffff ; modified""")
|
||||
CpuRegister.Y -> asmgen.out("""
|
||||
lda $targetName
|
||||
sta (+) +1
|
||||
lda $targetName+1
|
||||
sta (+) +2
|
||||
+ sty ${'$'}ffff ; modified""")
|
||||
}
|
||||
val pointerVarName = asmgen.asmIdentifierName(addressExpr)
|
||||
asmgen.out("""
|
||||
lda $pointerVarName
|
||||
sta ${C64Zeropage.SCRATCH_W2}
|
||||
lda $pointerVarName+1
|
||||
sta ${C64Zeropage.SCRATCH_W2+1}
|
||||
lda $ldaInstructionArg
|
||||
ldy #0
|
||||
sta (${C64Zeropage.SCRATCH_W2}),y""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.saveRegister(register)
|
||||
asmgen.translateExpression(addressExpr)
|
||||
asmgen.restoreRegister(register)
|
||||
when (register) {
|
||||
CpuRegister.A -> asmgen.out(" tay")
|
||||
CpuRegister.X -> throw AssemblyError("can't use X register here")
|
||||
CpuRegister.Y -> {}
|
||||
}
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
sta (+) +1
|
||||
lda $ESTACK_HI_HEX,x
|
||||
sta (+) +2
|
||||
+ sty ${'$'}ffff ; modified
|
||||
""")
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
sta ${C64Zeropage.SCRATCH_W2}
|
||||
lda $ESTACK_HI_HEX,x
|
||||
sta ${C64Zeropage.SCRATCH_W2+1}
|
||||
lda $ldaInstructionArg
|
||||
ldy #0
|
||||
sta (${C64Zeropage.SCRATCH_W2}),y""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromWordConstant(target: AssignTarget, word: Int) {
|
||||
private fun storeRegisterInMemoryAddress(register: CpuRegister, memoryAddress: DirectMemoryWrite) {
|
||||
// this is optimized for register A.
|
||||
val addressExpr = memoryAddress.addressExpression
|
||||
val addressLv = addressExpr as? NumericLiteralValue
|
||||
val registerName = register.name.toLowerCase()
|
||||
when {
|
||||
addressLv != null -> {
|
||||
asmgen.out(" st$registerName ${addressLv.number.toHex()}")
|
||||
}
|
||||
addressExpr is IdentifierReference -> {
|
||||
val targetName = asmgen.asmIdentifierName(addressExpr)
|
||||
when (register) {
|
||||
CpuRegister.A -> {}
|
||||
CpuRegister.X -> asmgen.out(" txa")
|
||||
CpuRegister.Y -> asmgen.out(" tya")
|
||||
}
|
||||
asmgen.out("""
|
||||
ldy $targetName
|
||||
sty ${C64Zeropage.SCRATCH_W1}
|
||||
ldy $targetName+1
|
||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
||||
ldy #0
|
||||
sta (${C64Zeropage.SCRATCH_W1}),y""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.saveRegister(register)
|
||||
asmgen.translateExpression(addressExpr)
|
||||
asmgen.restoreRegister(CpuRegister.A)
|
||||
asmgen.out("""
|
||||
inx
|
||||
ldy $ESTACK_LO_HEX,x
|
||||
sty ${C64Zeropage.SCRATCH_W1}
|
||||
ldy $ESTACK_HI_HEX,x
|
||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
||||
ldy #0
|
||||
sta (${C64Zeropage.SCRATCH_W1}),y""")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignFromWordConstant(target: AssignTarget, word: Int) {
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
when {
|
||||
@@ -450,7 +467,6 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
targetArrayIdx != null -> {
|
||||
val index = targetArrayIdx.arrayspec.index
|
||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||
// TODO optimize common cases
|
||||
asmgen.translateExpression(index)
|
||||
asmgen.out("""
|
||||
inx
|
||||
@@ -467,22 +483,21 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromByteConstant(target: AssignTarget, byte: Short) {
|
||||
private fun assignFromByteConstant(target: AssignTarget, byte: Short) {
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
val targetMemory = target.memoryAddress
|
||||
when {
|
||||
targetIdent != null -> {
|
||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||
asmgen.out(" lda #${byte.toHex()} | sta $targetName ")
|
||||
}
|
||||
target.memoryAddress != null -> {
|
||||
asmgen.out(" ldy #${byte.toHex()}")
|
||||
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
|
||||
targetMemory != null -> {
|
||||
storeByteViaRegisterAInMemoryAddress("#${byte.toHex()}", targetMemory)
|
||||
}
|
||||
targetArrayIdx != null -> {
|
||||
val index = targetArrayIdx.arrayspec.index
|
||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||
// TODO optimize common cases
|
||||
asmgen.translateExpression(index)
|
||||
asmgen.out("""
|
||||
inx
|
||||
@@ -495,7 +510,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromFloatConstant(target: AssignTarget, float: Double) {
|
||||
private fun assignFromFloatConstant(target: AssignTarget, float: Double) {
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
if (float == 0.0) {
|
||||
@@ -594,9 +609,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
}
|
||||
}
|
||||
|
||||
internal fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) {
|
||||
private fun assignFromMemoryByte(target: AssignTarget, address: Int?, identifier: IdentifierReference?) {
|
||||
val targetIdent = target.identifier
|
||||
val targetArrayIdx = target.arrayindexed
|
||||
val targetMemory = target.memoryAddress
|
||||
if (address != null) {
|
||||
when {
|
||||
targetIdent != null -> {
|
||||
@@ -606,9 +622,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
sta $targetName
|
||||
""")
|
||||
}
|
||||
target.memoryAddress != null -> {
|
||||
asmgen.out(" ldy ${address.toHex()}")
|
||||
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
|
||||
targetMemory != null -> {
|
||||
storeByteViaRegisterAInMemoryAddress(address.toHex(), targetMemory)
|
||||
}
|
||||
targetArrayIdx != null -> {
|
||||
val index = targetArrayIdx.arrayspec.index
|
||||
@@ -624,15 +639,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta (+) + 1
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
lda $sourceName+1
|
||||
sta (+) + 2
|
||||
+ lda ${'$'}ffff\t; modified
|
||||
sta ${C64Zeropage.SCRATCH_W1+1}
|
||||
ldy #0
|
||||
lda (${C64Zeropage.SCRATCH_W1}),y
|
||||
sta $targetName""")
|
||||
}
|
||||
target.memoryAddress != null -> {
|
||||
asmgen.out(" ldy $sourceName")
|
||||
storeRegisterInMemoryAddress(CpuRegister.Y, target.memoryAddress)
|
||||
targetMemory != null -> {
|
||||
storeByteViaRegisterAInMemoryAddress(sourceName, targetMemory)
|
||||
}
|
||||
targetArrayIdx != null -> {
|
||||
val index = targetArrayIdx.arrayspec.index
|
||||
@@ -663,13 +678,4 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
throw AssemblyError("weird array type")
|
||||
}
|
||||
}
|
||||
|
||||
fun assignToRegister(reg: CpuRegister, value: Short?, identifier: IdentifierReference?) {
|
||||
if(value!=null) {
|
||||
asmgen.out(" ld${reg.toString().toLowerCase()} #${value.toHex()}")
|
||||
} else if(identifier!=null) {
|
||||
val name = asmgen.asmIdentifierName(identifier)
|
||||
asmgen.out(" ld${reg.toString().toLowerCase()} $name")
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -35,6 +35,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
|
||||
when (functionName) {
|
||||
"msb" -> funcMsb(fcall)
|
||||
"lsb" -> funcLsb(fcall)
|
||||
"mkword" -> funcMkword(fcall, func)
|
||||
"abs" -> funcAbs(fcall, func)
|
||||
"swap" -> funcSwap(fcall)
|
||||
@@ -584,7 +585,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||
throw AssemblyError("msb required word argument")
|
||||
if (arg is NumericLiteralValue)
|
||||
throw AssemblyError("should have been const-folded")
|
||||
throw AssemblyError("msb(const) should have been const-folded away")
|
||||
if (arg is IdentifierReference) {
|
||||
val sourceName = asmgen.asmIdentifierName(arg)
|
||||
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
||||
@@ -594,6 +595,21 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcLsb(fcall: IFunctionCall) {
|
||||
val arg = fcall.args.single()
|
||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||
throw AssemblyError("lsb required word argument")
|
||||
if (arg is NumericLiteralValue)
|
||||
throw AssemblyError("lsb(const) should have been const-folded away")
|
||||
if (arg is IdentifierReference) {
|
||||
val sourceName = asmgen.asmIdentifierName(arg)
|
||||
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | dex")
|
||||
} else {
|
||||
asmgen.translateExpression(arg)
|
||||
// just ignore any high-byte
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputPushAddressAndLenghtOfArray(arg: Expression) {
|
||||
arg as IdentifierReference
|
||||
val identifierName = asmgen.asmIdentifierName(arg)
|
||||
|
||||
@@ -140,16 +140,17 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta (+) +1
|
||||
sta ${C64MachineDefinition.C64Zeropage.SCRATCH_W1}
|
||||
lda $sourceName+1
|
||||
sta (+) +2
|
||||
+ lda ${'$'}ffff ; modified
|
||||
sta ${C64MachineDefinition.C64Zeropage.SCRATCH_W1+1}
|
||||
ldy #0
|
||||
lda (${C64MachineDefinition.C64Zeropage.SCRATCH_W1}),y
|
||||
sta $ESTACK_LO_HEX,x
|
||||
dex""")
|
||||
}
|
||||
else -> {
|
||||
translateExpression(expr.addressExpression)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address")
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack")
|
||||
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.SubroutineParameter
|
||||
import prog8.compiler.AssemblyError
|
||||
@@ -107,48 +108,9 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
val paramVar = parameter.value
|
||||
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
|
||||
val target = AssignTarget(IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
|
||||
target.linkParents(value.parent)
|
||||
when (value) {
|
||||
is NumericLiteralValue -> {
|
||||
// optimize when the argument is a constant literal
|
||||
when(parameter.value.type) {
|
||||
in ByteDatatypes -> asmgen.assignFromByteConstant(target, value.number.toShort())
|
||||
in WordDatatypes -> asmgen.assignFromWordConstant(target, value.number.toInt())
|
||||
DataType.FLOAT -> asmgen.assignFromFloatConstant(target, value.number.toDouble())
|
||||
else -> throw AssemblyError("weird parameter datatype")
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
// optimize when the argument is a variable
|
||||
when (parameter.value.type) {
|
||||
in ByteDatatypes -> asmgen.assignFromByteVariable(target, value)
|
||||
in WordDatatypes -> asmgen.assignFromWordVariable(target, value)
|
||||
DataType.FLOAT -> asmgen.assignFromFloatVariable(target, value)
|
||||
in PassByReferenceDatatypes -> asmgen.assignFromAddressOf(target, value)
|
||||
else -> throw AssemblyError("weird parameter datatype")
|
||||
}
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
when(value.addressExpression) {
|
||||
is NumericLiteralValue -> {
|
||||
val address = (value.addressExpression as NumericLiteralValue).number.toInt()
|
||||
asmgen.assignFromMemoryByte(target, address, null)
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
asmgen.assignFromMemoryByte(target, null, value.addressExpression as IdentifierReference)
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateExpression(value.addressExpression)
|
||||
asmgen.out(" jsr prog8_lib.read_byte_from_address | inx")
|
||||
asmgen.assignFromRegister(target, CpuRegister.A)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
asmgen.translateExpression(value)
|
||||
asmgen.assignFromEvalResult(target)
|
||||
}
|
||||
}
|
||||
val assign = Assignment(target, value, value.position)
|
||||
assign.linkParents(value.parent)
|
||||
asmgen.translate(assign)
|
||||
}
|
||||
|
||||
private fun argumentViaRegister(sub: Subroutine, parameter: IndexedValue<SubroutineParameter>, value: Expression) {
|
||||
|
||||
@@ -1,744 +0,0 @@
|
||||
package prog8.compiler.target.c64.codegen
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.toHex
|
||||
|
||||
|
||||
// OLD inplace-assignment code.
|
||||
// should really come up with a more compact way to generate this kind of code...
|
||||
|
||||
/***
|
||||
|
||||
internal class InplaceAssignmentAsmGen(private val program: Program, private val errors: ErrorReporter, private val asmgen: AsmGen) {
|
||||
|
||||
internal fun translate(assign: Assignment) {
|
||||
if (assign.aug_op == null)
|
||||
translateNormalAssignment(assign)
|
||||
else
|
||||
translateInplaceAssignment(assign)
|
||||
}
|
||||
|
||||
private fun translateInplaceAssignment(assign: Assignment) {
|
||||
require(assign.aug_op != null)
|
||||
|
||||
when {
|
||||
assign.target.identifier != null -> {
|
||||
if (inplaceAssignToIdentifier(assign))
|
||||
return
|
||||
}
|
||||
assign.target.memoryAddress != null -> {
|
||||
if (inplaceAssignToMemoryByte(assign))
|
||||
return
|
||||
}
|
||||
assign.target.arrayindexed != null -> {
|
||||
if (inplaceAssignToArrayOrString(assign))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// TODO this is the slow FALLBACK, eventually we don't want to have to use it anymore:
|
||||
errors.warn("using suboptimal in-place assignment code (this should still be optimized)", assign.position)
|
||||
val normalAssignment = assign.asDesugaredNonaugmented()
|
||||
return translateNormalAssignment(normalAssignment)
|
||||
}
|
||||
|
||||
private fun inplaceAssignToArrayOrString(assign: Assignment): Boolean {
|
||||
val targetArray = assign.target.arrayindexed!!
|
||||
val arrayName = targetArray.identifier
|
||||
val arrayIndex = targetArray.arrayspec.index
|
||||
val targetName = asmgen.asmIdentifierName(arrayName)
|
||||
val arrayDt = arrayName.targetVarDecl(program.namespace)!!.datatype
|
||||
val constValue = assign.value.constValue(program)?.number
|
||||
if (constValue != null) {
|
||||
// constant value to set in array
|
||||
val hexValue = constValue.toHex()
|
||||
if (assign.aug_op == "setvalue") {
|
||||
when (arrayDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoY(targetArray)
|
||||
asmgen.out(" lda #$hexValue | sta $targetName,y")
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" lda #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoA(targetArray)
|
||||
asmgen.out("""
|
||||
asl a
|
||||
tay
|
||||
lda #<$hexValue
|
||||
sta $targetName,y
|
||||
lda #>$hexValue
|
||||
sta $targetName+1,y
|
||||
""")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
assignFromFloatConstant(assign.target, constValue.toDouble())
|
||||
}
|
||||
else -> throw AssemblyError("assignment to array: invalid array dt $arrayDt")
|
||||
}
|
||||
} else {
|
||||
TODO("aug assignment to element in array/string")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// non-const value.
|
||||
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
|
||||
when (assign.value) {
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
|
||||
when(arrayDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> {
|
||||
asmgen.out(" lda $sourceName")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoY(targetArray)
|
||||
asmgen.out(" sta $targetName,y")
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" lda #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoA(targetArray)
|
||||
asmgen.out("""
|
||||
asl a
|
||||
tay
|
||||
lda $sourceName
|
||||
sta $targetName,y
|
||||
lda $sourceName+1
|
||||
sta $targetName+1,y
|
||||
""")
|
||||
}
|
||||
DataType.ARRAY_F -> return false // TODO optimize instead of fallback?
|
||||
else -> throw AssemblyError("assignment to array: invalid array dt $arrayDt")
|
||||
}
|
||||
return true
|
||||
}
|
||||
is AddressOf -> {
|
||||
TODO("assign address into array $assign")
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
TODO("assign memory read into array $assign")
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
if(assign.aug_op != "setvalue")
|
||||
return false // we don't put effort into optimizing anything beside simple assignment
|
||||
val valueArrayExpr = assign.value as ArrayIndexedExpression
|
||||
val valueArrayIndex = valueArrayExpr.arrayspec.index
|
||||
val valueVariablename = asmgen.asmIdentifierName(valueArrayExpr.identifier)
|
||||
// val valueDt = valueArrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
when(arrayDt) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.STR -> {
|
||||
if (valueArrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #${valueArrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoY(valueArrayExpr)
|
||||
asmgen.out(" lda $valueVariablename,y")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoY(targetArray)
|
||||
asmgen.out(" sta $targetName,y")
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if (valueArrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #2*${valueArrayIndex.number.toHex()}")
|
||||
else {
|
||||
asmgen.translateArrayIndexIntoA(valueArrayExpr)
|
||||
asmgen.out(" asl a | tay")
|
||||
}
|
||||
asmgen.out("""
|
||||
lda $valueVariablename,y
|
||||
pha
|
||||
lda $valueVariablename+1,y
|
||||
pha
|
||||
""")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #2*${arrayIndex.number.toHex()}")
|
||||
else {
|
||||
asmgen.translateArrayIndexIntoA(targetArray)
|
||||
asmgen.out(" asl a | tay")
|
||||
}
|
||||
asmgen.out("""
|
||||
pla
|
||||
sta $targetName+1,y
|
||||
pla
|
||||
sta $targetName,y
|
||||
""")
|
||||
return true
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
if (valueArrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #5*${valueArrayIndex.number.toHex()}")
|
||||
else {
|
||||
asmgen.translateArrayIndexIntoA(valueArrayExpr)
|
||||
asmgen.out("""
|
||||
sta ${C64Zeropage.SCRATCH_REG}
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc ${C64Zeropage.SCRATCH_REG}
|
||||
tay
|
||||
""")
|
||||
}
|
||||
asmgen.out("""
|
||||
lda $valueVariablename,y
|
||||
pha
|
||||
lda $valueVariablename+1,y
|
||||
pha
|
||||
lda $valueVariablename+2,y
|
||||
pha
|
||||
lda $valueVariablename+3,y
|
||||
pha
|
||||
lda $valueVariablename+4,y
|
||||
pha
|
||||
""")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #5*${arrayIndex.number.toHex()}")
|
||||
else {
|
||||
asmgen.translateArrayIndexIntoA(targetArray)
|
||||
asmgen.out("""
|
||||
sta ${C64Zeropage.SCRATCH_REG}
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc ${C64Zeropage.SCRATCH_REG}
|
||||
tay
|
||||
""")
|
||||
}
|
||||
asmgen.out("""
|
||||
pla
|
||||
sta $targetName+4,y
|
||||
pla
|
||||
sta $targetName+3,y
|
||||
pla
|
||||
sta $targetName+2,y
|
||||
pla
|
||||
sta $targetName+1,y
|
||||
pla
|
||||
sta $targetName,y
|
||||
""")
|
||||
return true
|
||||
}
|
||||
else -> throw AssemblyError("assignment to array: invalid array dt")
|
||||
}
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
fallbackAssignment(assign)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun inplaceAssignToMemoryByte(assign: Assignment): Boolean {
|
||||
val address = assign.target.memoryAddress?.addressExpression?.constValue(program)?.number
|
||||
?: return inplaceAssignToNonConstMemoryByte(assign)
|
||||
|
||||
val hexAddr = address.toHex()
|
||||
val constValue = assign.value.constValue(program)
|
||||
if (constValue != null) {
|
||||
val hexValue = constValue.number.toHex()
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda #$hexValue | sta $hexAddr")
|
||||
"+=" -> asmgen.out(" lda $hexAddr | clc | adc #$hexValue | sta $hexAddr")
|
||||
"-=" -> asmgen.out(" lda $hexAddr | sec | sbc #$hexValue | sta $hexAddr")
|
||||
"/=" -> TODO("membyte /= const $hexValue")
|
||||
"*=" -> TODO("membyte *= const $hexValue")
|
||||
"&=" -> asmgen.out(" lda $hexAddr | and #$hexValue | sta $hexAddr")
|
||||
"|=" -> asmgen.out(" lda $hexAddr | ora #$hexValue | sta $hexAddr")
|
||||
"^=" -> asmgen.out(" lda $hexAddr | eor #$hexValue | sta $hexAddr")
|
||||
"%=" -> TODO("membyte %= const $hexValue")
|
||||
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
|
||||
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
|
||||
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// non-const value.
|
||||
when (assign.value) {
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
|
||||
when(assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda $sourceName | sta $hexAddr")
|
||||
else -> TODO("membyte aug.assign variable $assign")
|
||||
}
|
||||
return true
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
val memory = (assign.value as DirectMemoryRead).addressExpression.constValue(program)!!.number.toHex()
|
||||
when(assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda $memory | sta $hexAddr")
|
||||
else -> TODO("membyte aug.assign memread $assign")
|
||||
}
|
||||
return true
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
TODO("membyte = array value $assign")
|
||||
}
|
||||
is AddressOf -> throw AssemblyError("can't assign address to byte")
|
||||
else -> {
|
||||
fallbackAssignment(assign)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun inplaceAssignToNonConstMemoryByte(assign: Assignment): Boolean {
|
||||
// target address is not constant, so evaluate it from the stack
|
||||
asmgen.translateExpression(assign.target.memoryAddress!!.addressExpression)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
lda $ESTACK_HI_HEX,x
|
||||
sta ${C64Zeropage.SCRATCH_W1}+1
|
||||
""")
|
||||
|
||||
val constValue = assign.value.constValue(program)
|
||||
if (constValue != null) {
|
||||
val hexValue = constValue.number.toHex()
|
||||
asmgen.out(" ldy #0")
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
|
||||
"+=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | clc | adc #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
|
||||
"-=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | sec | sbc #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
|
||||
"/=" -> TODO("membyte /= const $hexValue")
|
||||
"*=" -> TODO("membyte *= const $hexValue")
|
||||
"&=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | and #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
|
||||
"|=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | ora #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
|
||||
"^=" -> asmgen.out(" lda (${C64Zeropage.SCRATCH_W1}),y | eor #$hexValue | sta (${C64Zeropage.SCRATCH_W1}),y")
|
||||
"%=" -> TODO("membyte %= const $hexValue")
|
||||
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
|
||||
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
|
||||
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// non-const value.
|
||||
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
|
||||
when (assign.value) {
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
|
||||
TODO("membyte = variable $assign")
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
TODO("membyte = memread $assign")
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
if (assign.aug_op == "setvalue") {
|
||||
val arrayExpr = assign.value as ArrayIndexedExpression
|
||||
val arrayIndex = arrayExpr.arrayspec.index
|
||||
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if (arrayDt != DataType.ARRAY_B && arrayDt != DataType.ARRAY_UB && arrayDt != DataType.STR)
|
||||
throw AssemblyError("assign to memory byte: expected byte array or string source")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoY(arrayExpr)
|
||||
asmgen.out("""
|
||||
lda $variablename,y
|
||||
ldy #0
|
||||
sta (${C64Zeropage.SCRATCH_W1}),y
|
||||
""")
|
||||
} else {
|
||||
// TODO optimize more augmented assignment cases
|
||||
val normalAssign = assign.asDesugaredNonaugmented()
|
||||
asmgen.translateExpression(normalAssign.value)
|
||||
assignFromEvalResult(normalAssign.target)
|
||||
}
|
||||
return true
|
||||
}
|
||||
is AddressOf -> throw AssemblyError("can't assign memory address to memory byte")
|
||||
else -> {
|
||||
fallbackAssignment(assign)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false // TODO optimized
|
||||
}
|
||||
|
||||
private fun inplaceAssignToIdentifier(assign: Assignment): Boolean {
|
||||
val targetType = assign.target.inferType(program, assign)
|
||||
val constNumber = assign.value.constValue(program)?.number
|
||||
val targetName = asmgen.asmIdentifierName(assign.target.identifier!!)
|
||||
|
||||
when (targetType.typeOrElse(DataType.STRUCT)) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
// (u)byte assignment
|
||||
if (constNumber != null) {
|
||||
val hexValue = constNumber.toHex()
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda #$hexValue | sta $targetName")
|
||||
"+=" -> asmgen.out(" lda $targetName | clc | adc #$hexValue | sta $targetName")
|
||||
"-=" -> asmgen.out(" lda $targetName | sec | sbc #$hexValue | sta $targetName")
|
||||
"/=" -> TODO("variable /= const $hexValue")
|
||||
"*=" -> TODO("variable *= const $hexValue")
|
||||
"&=" -> asmgen.out(" lda $targetName | and #$hexValue | sta $targetName")
|
||||
"|=" -> asmgen.out(" lda $targetName | ora #$hexValue | sta $targetName")
|
||||
"^=" -> asmgen.out(" lda $targetName | eor #$hexValue | sta $targetName")
|
||||
"%=" -> TODO("variable %= const $hexValue")
|
||||
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
|
||||
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
|
||||
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// non-const (u)byte value
|
||||
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
|
||||
when (assign.value) {
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda $sourceName | sta $targetName")
|
||||
"+=" -> asmgen.out(" lda $targetName | clc | adc $sourceName | sta $targetName")
|
||||
"-=" -> asmgen.out(" lda $targetName | sec | sbc $sourceName | sta $targetName")
|
||||
"/=" -> TODO("variable /= variable")
|
||||
"*=" -> TODO("variable *= variable")
|
||||
"&=" -> asmgen.out(" lda $targetName | and $sourceName | sta $targetName")
|
||||
"|=" -> asmgen.out(" lda $targetName | ora $sourceName | sta $targetName")
|
||||
"^=" -> asmgen.out(" lda $targetName | eor $sourceName | sta $targetName")
|
||||
"%=" -> TODO("variable %= variable")
|
||||
"<<=" -> throw AssemblyError("<<= should have been replaced by lsl()")
|
||||
">>=" -> throw AssemblyError("<<= should have been replaced by lsr()")
|
||||
else -> throw AssemblyError("invalid aug_op ${assign.aug_op}")
|
||||
}
|
||||
return true
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
TODO("variable = memory read $assign")
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
if (assign.aug_op == "setvalue") {
|
||||
val arrayExpr = assign.value as ArrayIndexedExpression
|
||||
val arrayIndex = arrayExpr.arrayspec.index
|
||||
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if (arrayDt != DataType.ARRAY_B && arrayDt != DataType.ARRAY_UB && arrayDt != DataType.STR)
|
||||
throw AssemblyError("assign to identifier: expected byte array or string source")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" ldy #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoY(arrayExpr)
|
||||
asmgen.out(" lda $variablename,y | sta $targetName")
|
||||
} else {
|
||||
// TODO optimize more augmented assignment cases
|
||||
val normalAssign = assign.asDesugaredNonaugmented()
|
||||
asmgen.translateExpression(normalAssign.value)
|
||||
assignFromEvalResult(normalAssign.target)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
fallbackAssignment(assign)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if (constNumber != null) {
|
||||
val hexNumber = constNumber.toHex()
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> {
|
||||
asmgen.out("""
|
||||
lda #<$hexNumber
|
||||
sta $targetName
|
||||
lda #>$hexNumber
|
||||
sta $targetName+1
|
||||
""")
|
||||
}
|
||||
"+=" -> {
|
||||
asmgen.out("""
|
||||
lda $targetName
|
||||
clc
|
||||
adc #<$hexNumber
|
||||
sta $targetName
|
||||
lda $targetName+1
|
||||
adc #>$hexNumber
|
||||
sta $targetName+1
|
||||
""")
|
||||
}
|
||||
"-=" -> {
|
||||
asmgen.out("""
|
||||
lda $targetName
|
||||
sec
|
||||
sbc #<$hexNumber
|
||||
sta $targetName
|
||||
lda $targetName+1
|
||||
sbc #>$hexNumber
|
||||
sta $targetName+1
|
||||
""")
|
||||
}
|
||||
else -> TODO("variable aug.assign ${assign.aug_op} const $hexNumber")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// non-const value
|
||||
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
|
||||
when (assign.value) {
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> {
|
||||
asmgen.out("""
|
||||
lda $sourceName
|
||||
sta $targetName
|
||||
lda $sourceName+1
|
||||
sta $targetName+1
|
||||
""")
|
||||
}
|
||||
"+=" -> {
|
||||
asmgen.out("""
|
||||
lda $targetName
|
||||
clc
|
||||
adc $sourceName
|
||||
sta $targetName
|
||||
lda $targetName+1
|
||||
adc $sourceName+1
|
||||
sta $targetName+1
|
||||
""")
|
||||
return true
|
||||
}
|
||||
"-=" -> {
|
||||
asmgen.out("""
|
||||
lda $targetName
|
||||
sec
|
||||
sbc $sourceName
|
||||
sta $targetName
|
||||
lda $targetName+1
|
||||
sbc $sourceName+1
|
||||
sta $targetName+1
|
||||
""")
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
TODO("variable aug.assign variable")
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
is DirectMemoryRead -> throw AssemblyError("expected a typecast for assigning memory read byte to word")
|
||||
is AddressOf -> {
|
||||
val name = asmgen.asmIdentifierName((assign.value as AddressOf).identifier)
|
||||
asmgen.out(" lda #<$name | sta $targetName | lda #>$name | sta $targetName+1")
|
||||
return true
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
if (assign.aug_op == "setvalue") {
|
||||
val arrayExpr = assign.value as ArrayIndexedExpression
|
||||
val arrayIndex = arrayExpr.arrayspec.index
|
||||
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if (arrayDt != DataType.ARRAY_W && arrayDt != DataType.ARRAY_UW)
|
||||
throw AssemblyError("assign to identifier: expected word array source")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" lda #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoA(arrayExpr)
|
||||
asmgen.out("""
|
||||
asl a
|
||||
tay
|
||||
lda $variablename,y
|
||||
sta $targetName
|
||||
lda $variablename+1,y
|
||||
sta $targetName+1
|
||||
""")
|
||||
} else {
|
||||
// TODO optimize more augmented assignment cases
|
||||
val normalAssign = assign.asDesugaredNonaugmented()
|
||||
asmgen.translateExpression(normalAssign.value)
|
||||
assignFromEvalResult(normalAssign.target)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
fallbackAssignment(assign)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if (constNumber != null) {
|
||||
// assign a constant
|
||||
val floatConst = asmgen.getFloatConst(constNumber.toDouble())
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> assignFromFloatConstant(assign.target, constNumber.toDouble())
|
||||
"+=" -> {
|
||||
if (constNumber == 0.5) {
|
||||
asmgen.out("""
|
||||
lda #<$targetName
|
||||
ldy #>$targetName
|
||||
jsr c64flt.MOVFM
|
||||
jsr c64flt.FADDH
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
ldx #<$targetName
|
||||
ldy #>$targetName
|
||||
jsr c64flt.MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda #<$targetName
|
||||
ldy #>$targetName
|
||||
jsr c64flt.MOVFM
|
||||
lda #<$floatConst
|
||||
ldy #>$floatConst
|
||||
jsr c64flt.FADD
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
ldx #<$targetName
|
||||
ldy #>$targetName
|
||||
jsr c64flt.MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
""")
|
||||
}
|
||||
return true
|
||||
}
|
||||
"-=" -> {
|
||||
asmgen.out("""
|
||||
lda #<$floatConst
|
||||
ldy #>$floatConst
|
||||
jsr c64flt.MOVFM
|
||||
lda #<$targetName
|
||||
ldy #>$targetName
|
||||
jsr c64flt.FSUB
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
ldx #<$targetName
|
||||
ldy #>$targetName
|
||||
jsr c64flt.MOVMF
|
||||
ldx c64.SCRATCH_ZPREGX
|
||||
""")
|
||||
return true
|
||||
}
|
||||
else -> TODO("float const value aug.assign $assign")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// non-const float value.
|
||||
// !!! DON'T FORGET : CAN BE AUGMENTED ASSIGNMENT !!!
|
||||
when (assign.value) {
|
||||
is IdentifierReference -> {
|
||||
when (assign.aug_op) {
|
||||
"setvalue" -> assignFromFloatVariable(assign.target, assign.value as IdentifierReference)
|
||||
"+=" -> return false // TODO optimized float += variable
|
||||
"-=" -> return false // TODO optimized float -= variable
|
||||
else -> TODO("float non-const value aug.assign $assign")
|
||||
}
|
||||
return true
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
when(assign.aug_op) {
|
||||
"setvalue" -> {
|
||||
val arrayExpr = assign.value as ArrayIndexedExpression
|
||||
val arrayIndex = arrayExpr.arrayspec.index
|
||||
val variablename = asmgen.asmIdentifierName(arrayExpr.identifier)
|
||||
val arrayDt = arrayExpr.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||
if (arrayDt != DataType.ARRAY_F)
|
||||
throw AssemblyError("assign to identifier: expected float array source")
|
||||
if (arrayIndex is NumericLiteralValue)
|
||||
asmgen.out(" lda #${arrayIndex.number.toHex()}")
|
||||
else
|
||||
asmgen.translateArrayIndexIntoA(arrayExpr)
|
||||
asmgen.out("""
|
||||
sta c64.SCRATCH_ZPB1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc c64.SCRATCH_ZPB1
|
||||
tay
|
||||
lda $variablename,y
|
||||
sta $targetName
|
||||
lda $variablename+1,y
|
||||
sta $targetName+1
|
||||
lda $variablename+2,y
|
||||
sta $targetName+2
|
||||
lda $variablename+3,y
|
||||
sta $targetName+3
|
||||
lda $variablename+4,y
|
||||
sta $targetName+4
|
||||
""")
|
||||
}
|
||||
else -> TODO("float $assign")
|
||||
}
|
||||
return true
|
||||
}
|
||||
else -> {
|
||||
fallbackAssignment(assign)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
DataType.STR -> {
|
||||
val identifier = assign.value as? IdentifierReference
|
||||
?: throw AssemblyError("string value assignment expects identifier value")
|
||||
val sourceName = asmgen.asmIdentifierName(identifier)
|
||||
asmgen.out("""
|
||||
lda #<$targetName
|
||||
sta ${C64Zeropage.SCRATCH_W1}
|
||||
lda #>$targetName
|
||||
sta ${C64Zeropage.SCRATCH_W1+1}
|
||||
lda #<$sourceName
|
||||
ldy #>$sourceName
|
||||
jsr prog8_lib.strcpy
|
||||
""")
|
||||
return true
|
||||
}
|
||||
else -> throw AssemblyError("assignment to identifier: invalid target datatype: $targetType")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun fallbackAssignment(assign: Assignment) {
|
||||
if (assign.aug_op != "setvalue") {
|
||||
/* stack-based evaluation of the expression is required */
|
||||
val normalAssign = assign.asDesugaredNonaugmented()
|
||||
asmgen.translateExpression(normalAssign.value)
|
||||
assignFromEvalResult(normalAssign.target)
|
||||
} else {
|
||||
when (assign.value) {
|
||||
is FunctionCall -> {
|
||||
// TODO is there a way to avoid function return value being passed via the stack?
|
||||
// for instance, 1 byte return value always in A, etc
|
||||
val normalAssign = assign.asDesugaredNonaugmented()
|
||||
asmgen.translateExpression(normalAssign.value)
|
||||
assignFromEvalResult(normalAssign.target)
|
||||
}
|
||||
else -> {
|
||||
/* stack-based evaluation of the expression is required */
|
||||
val normalAssign = assign.asDesugaredNonaugmented()
|
||||
asmgen.translateExpression(normalAssign.value)
|
||||
assignFromEvalResult(normalAssign.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
***/
|
||||
@@ -7,6 +7,8 @@ import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.PostIncrDecr
|
||||
import prog8.compiler.AssemblyError
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||
import prog8.compiler.toHex
|
||||
|
||||
|
||||
@@ -53,7 +55,20 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
else -> throw AssemblyError("weird target type $targetMemory")
|
||||
else -> {
|
||||
asmgen.translateExpression(addressExpr)
|
||||
asmgen.out("""
|
||||
inx
|
||||
lda $ESTACK_LO_HEX,x
|
||||
sta (+) + 1
|
||||
lda $ESTACK_HI_HEX,x
|
||||
sta (+) + 2
|
||||
""")
|
||||
if(incr)
|
||||
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||
else
|
||||
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||
}
|
||||
}
|
||||
}
|
||||
targetArrayIdx!=null -> {
|
||||
|
||||
@@ -3,6 +3,8 @@ package prog8.functions
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.CompilerException
|
||||
import kotlin.math.*
|
||||
|
||||
@@ -35,6 +37,7 @@ val BuiltinFunctions = mapOf(
|
||||
"sum" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||
"sizeof" to FSignature(true, listOf(FParam("object", DataType.values().toSet())), DataType.UBYTE, ::builtinSizeof),
|
||||
// normal functions follow:
|
||||
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||
"sin" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||
@@ -240,6 +243,42 @@ private fun builtinAbs(args: List<Expression>, position: Position, program: Prog
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinSizeof(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
// 1 arg, type = anything, result type = ubyte
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("sizeof requires one argument", position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("sizeof argument should be an identifier", position)
|
||||
|
||||
val dt = args[0].inferType(program)
|
||||
if(dt.isKnown) {
|
||||
val target = (args[0] as IdentifierReference).targetStatement(program.namespace)
|
||||
?: throw CannotEvaluateException("sizeof", "no target")
|
||||
|
||||
fun structSize(target: StructDecl) =
|
||||
NumericLiteralValue(DataType.UBYTE, target.statements.map { (it as VarDecl).datatype.memorySize() }.sum(), position)
|
||||
|
||||
return when {
|
||||
dt.typeOrElse(DataType.STRUCT) in ArrayDatatypes -> {
|
||||
val length = (target as VarDecl).arraysize!!.size() ?: throw CannotEvaluateException("sizeof", "unknown array size")
|
||||
val elementDt = ArrayElementTypes.getValue(dt.typeOrElse(DataType.STRUCT))
|
||||
numericLiteral(elementDt.memorySize() * length, position)
|
||||
}
|
||||
dt.istype(DataType.STRUCT) -> {
|
||||
when (target) {
|
||||
is VarDecl -> structSize(target.struct!!)
|
||||
is StructDecl -> structSize(target)
|
||||
else -> throw CompilerException("weird struct type $target")
|
||||
}
|
||||
}
|
||||
dt.istype(DataType.STR) -> throw SyntaxError("sizeof str is undefined, did you mean len?", position)
|
||||
else -> NumericLiteralValue(DataType.UBYTE, dt.typeOrElse(DataType.STRUCT).memorySize(), position)
|
||||
}
|
||||
} else {
|
||||
throw SyntaxError("sizeof invalid argument type", position)
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("strlen requires one argument", position)
|
||||
@@ -254,9 +293,6 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
val constArg = args[0].constValue(program)
|
||||
if(constArg!=null)
|
||||
throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
|
||||
val directMemVar = ((args[0] as? DirectMemoryRead)?.addressExpression as? IdentifierReference)?.targetVarDecl(program.namespace)
|
||||
var arraySize = directMemVar?.arraysize?.size()
|
||||
@@ -265,18 +301,12 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
if(args[0] is ArrayLiteralValue)
|
||||
return NumericLiteralValue.optimalInteger((args[0] as ArrayLiteralValue).value.size, position)
|
||||
if(args[0] !is IdentifierReference)
|
||||
throw SyntaxError("len argument should be an identifier, but is ${args[0]}", position)
|
||||
throw SyntaxError("len argument should be an identifier", position)
|
||||
val target = (args[0] as IdentifierReference).targetVarDecl(program.namespace)
|
||||
?: throw CannotEvaluateException("len", "no target vardecl")
|
||||
|
||||
return when(target.datatype) {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
arraySize = target.arraysize?.size()
|
||||
if(arraySize==null)
|
||||
throw CannotEvaluateException("len", "arraysize unknown")
|
||||
NumericLiteralValue.optimalInteger(arraySize, args[0].position)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F -> {
|
||||
arraySize = target.arraysize?.size()
|
||||
if(arraySize==null)
|
||||
throw CannotEvaluateException("len", "arraysize unknown")
|
||||
@@ -286,7 +316,8 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
|
||||
val refLv = target.value as StringLiteralValue
|
||||
NumericLiteralValue.optimalInteger(refLv.value.length, args[0].position)
|
||||
}
|
||||
in NumericDatatypes -> throw SyntaxError("len of weird argument ${args[0]}", position)
|
||||
DataType.STRUCT -> throw SyntaxError("cannot use len on struct, did you mean sizeof?", args[0].position)
|
||||
in NumericDatatypes -> throw SyntaxError("cannot use len on numeric value, did you mean sizeof?", args[0].position)
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
}
|
||||
@@ -381,8 +412,8 @@ private fun numericLiteral(value: Number, position: Position): NumericLiteralVal
|
||||
floatNum
|
||||
|
||||
return when(tweakedValue) {
|
||||
is Int -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
||||
is Short -> NumericLiteralValue.optimalNumeric(value.toInt(), position)
|
||||
is Int -> NumericLiteralValue.optimalInteger(value.toInt(), position)
|
||||
is Short -> NumericLiteralValue.optimalInteger(value.toInt(), position)
|
||||
is Byte -> NumericLiteralValue(DataType.UBYTE, value.toShort(), position)
|
||||
is Double -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||
is Float -> NumericLiteralValue(DataType.FLOAT, value.toDouble(), position)
|
||||
|
||||
@@ -132,11 +132,11 @@ class ConstExprEvaluator {
|
||||
private fun bitwiseand(left: NumericLiteralValue, right: NumericLiteralValue): NumericLiteralValue {
|
||||
if(left.type== DataType.UBYTE) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() or (right.number.toInt() and 255)).toShort(), left.position)
|
||||
return NumericLiteralValue(DataType.UBYTE, (left.number.toInt() and (right.number.toInt() and 255)).toShort(), left.position)
|
||||
}
|
||||
} else if(left.type== DataType.UWORD) {
|
||||
if(right.type in IntegerDatatypes) {
|
||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() or right.number.toInt(), left.position)
|
||||
return NumericLiteralValue(DataType.UWORD, left.number.toInt() and right.number.toInt(), left.position)
|
||||
}
|
||||
}
|
||||
throw ExpressionError("cannot calculate $left & $right", left.position)
|
||||
@@ -163,7 +163,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot add $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() + right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() + right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@@ -180,7 +180,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot subtract $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() - right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() - right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@@ -197,7 +197,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot multiply ${left.type} and ${right.type}"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalNumeric(left.number.toInt() * right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteralValue.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, left.number.toInt() * right.number.toDouble(), left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@@ -220,7 +220,7 @@ class ConstExprEvaluator {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
val result: Int = left.number.toInt() / right.number.toInt()
|
||||
NumericLiteralValue.optimalNumeric(result, left.position)
|
||||
NumericLiteralValue.optimalInteger(result, left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number.toDouble()==0.0) divideByZeroError(right.position)
|
||||
|
||||
@@ -171,7 +171,7 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
||||
if(declValue!=null && decl.type==VarDeclType.VAR
|
||||
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
||||
// cast the numeric literal to the appropriate datatype of the variable
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, declValue.cast(decl.datatype), decl))
|
||||
return listOf(IAstModification.ReplaceNode(decl.value!!, declValue.castNoCheck(decl.datatype), decl))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
@@ -203,7 +203,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
"-" -> when (subexpr.type) {
|
||||
in IntegerDatatypes -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position),
|
||||
NumericLiteralValue.optimalInteger(-subexpr.number.toInt(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
@@ -216,7 +216,7 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
"~" -> when (subexpr.type) {
|
||||
in IntegerDatatypes -> {
|
||||
listOf(IAstModification.ReplaceNode(expr,
|
||||
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position),
|
||||
NumericLiteralValue.optimalInteger(subexpr.number.toInt().inv(), subexpr.position),
|
||||
parent))
|
||||
}
|
||||
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
|
||||
@@ -273,11 +273,8 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
// const fold when both operands are a const
|
||||
if(leftconst != null && rightconst != null) {
|
||||
val evaluator = ConstExprEvaluator()
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr,
|
||||
evaluator.evaluate(leftconst, expr.operator, rightconst),
|
||||
parent
|
||||
))
|
||||
val result = evaluator.evaluate(leftconst, expr.operator, rightconst)
|
||||
return listOf(IAstModification.ReplaceNode(expr, result, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
@@ -323,13 +320,13 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
val newFrom: NumericLiteralValue
|
||||
val newTo: NumericLiteralValue
|
||||
try {
|
||||
newFrom = rangeFrom.cast(targetDt)
|
||||
newTo = rangeTo.cast(targetDt)
|
||||
newFrom = rangeFrom.castNoCheck(targetDt)
|
||||
newTo = rangeTo.castNoCheck(targetDt)
|
||||
} catch (x: ExpressionError) {
|
||||
return range
|
||||
}
|
||||
val newStep: Expression = try {
|
||||
stepLiteral?.cast(targetDt)?: range.step
|
||||
stepLiteral?.castNoCheck(targetDt)?: range.step
|
||||
} catch(ee: ExpressionError) {
|
||||
range.step
|
||||
}
|
||||
@@ -379,6 +376,18 @@ internal class ConstantFoldingOptimizer(private val program: Program) : AstWalke
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||
val numval = decl.value as? NumericLiteralValue
|
||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||
val valueDt = numval.inferType(program)
|
||||
if(!valueDt.istype(decl.datatype)) {
|
||||
val adjustedVal = numval.castNoCheck(decl.datatype)
|
||||
return listOf(IAstModification.ReplaceNode(numval, adjustedVal, decl))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private class ShuffleOperands(val expr: BinaryExpression,
|
||||
val exprOperator: String?,
|
||||
val subExpr: BinaryExpression,
|
||||
|
||||
@@ -29,20 +29,23 @@ internal class ExpressionSimplifier(private val program: Program) : AstWalker()
|
||||
// try to statically convert a literal value into one of the desired type
|
||||
val literal = typecast.expression as? NumericLiteralValue
|
||||
if (literal != null) {
|
||||
val newLiteral = literal.cast(typecast.type)
|
||||
val newLiteral = literal.castNoCheck(typecast.type)
|
||||
if (newLiteral !== literal)
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral, typecast)
|
||||
}
|
||||
|
||||
// remove redundant nested typecasts:
|
||||
// if the typecast casts a value to the same type, remove the cast.
|
||||
// if the typecast contains another typecast, remove the inner typecast.
|
||||
// remove redundant nested typecasts
|
||||
val subTypecast = typecast.expression as? TypecastExpression
|
||||
if (subTypecast != null) {
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
// remove the sub-typecast if its datatype is larger than the outer typecast
|
||||
if(subTypecast.type largerThan typecast.type) {
|
||||
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||
}
|
||||
} else {
|
||||
if (typecast.expression.inferType(program).istype(typecast.type))
|
||||
if (typecast.expression.inferType(program).istype(typecast.type)) {
|
||||
// remove duplicate cast
|
||||
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||
}
|
||||
}
|
||||
|
||||
return mods
|
||||
|
||||
@@ -136,7 +136,7 @@ internal class StatementOptimizer(private val program: Program,
|
||||
val subroutine = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is ReturnFromIrq || first is Return)
|
||||
if(first is Return)
|
||||
return listOf(IAstModification.Remove(functionCallStatement, parent))
|
||||
}
|
||||
|
||||
@@ -368,11 +368,12 @@ internal class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
if(binExpr.right isSameAs assignment.target) {
|
||||
if(binExpr.operator in setOf("+", "*", "&", "|")) {
|
||||
// associative operator, swap the operands so that the assignment target is first (left)
|
||||
|
||||
if(binExpr.operator in associativeOperators && binExpr.right isSameAs assignment.target) {
|
||||
// associative operator, swap the operands so that the assignment target is first (left)
|
||||
// unless the other operand is the same in which case we don't swap (endless loop!)
|
||||
if (!(binExpr.left isSameAs binExpr.right))
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -405,8 +406,8 @@ internal class StatementOptimizer(private val program: Program,
|
||||
if (cv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||
// replace by several INCs (a bit less when dealing with memory targets)
|
||||
if (vardeclDt != VarDeclType.MEMORY && cv in 1.0..4.0) {
|
||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||
val incs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(cv.toInt()) {
|
||||
incs.statements.add(PostIncrDecr(assignment.target, "++", assignment.position))
|
||||
@@ -419,8 +420,8 @@ internal class StatementOptimizer(private val program: Program,
|
||||
if (cv == 0.0) {
|
||||
return listOf(IAstModification.Remove(assignment, parent))
|
||||
} else if (targetDt in IntegerDatatypes && floor(cv) == cv) {
|
||||
if ((vardeclDt == VarDeclType.MEMORY && cv in 1.0..3.0) || (vardeclDt != VarDeclType.MEMORY && cv in 1.0..8.0)) {
|
||||
// replace by several DECs (a bit less when dealing with memory targets)
|
||||
if (vardeclDt != VarDeclType.MEMORY && cv in 1.0..4.0) {
|
||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||
val decs = AnonymousScope(mutableListOf(), assignment.position)
|
||||
repeat(cv.toInt()) {
|
||||
decs.statements.add(PostIncrDecr(assignment.target, "--", assignment.position))
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ErrorReporter
|
||||
import prog8.ast.processing.AstWalker
|
||||
import prog8.ast.processing.IAstModification
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.ast.statements.*
|
||||
|
||||
/*
|
||||
TODO: remove unreachable code after return and exit()
|
||||
*/
|
||||
|
||||
internal class UnusedCodeRemover: AstWalker() {
|
||||
internal class UnusedCodeRemover(private val errors: ErrorReporter): AstWalker() {
|
||||
|
||||
override fun before(program: Program, parent: Node): Iterable<IAstModification> {
|
||||
val callgraph = CallGraph(program)
|
||||
@@ -20,8 +18,9 @@ internal class UnusedCodeRemover: AstWalker() {
|
||||
val entrypoint = program.entrypoint()
|
||||
program.modules.forEach {
|
||||
callgraph.forAllSubroutines(it) { sub ->
|
||||
if (sub !== entrypoint && !sub.keepAlways && (callgraph.calledBy[sub].isNullOrEmpty() || (sub.containsNoCodeNorVars() && !sub.isAsmSubroutine)))
|
||||
if (sub !== entrypoint && !sub.isAsmSubroutine && (callgraph.calledBy[sub].isNullOrEmpty() || sub.containsNoCodeNorVars())) {
|
||||
removals.add(IAstModification.Remove(sub, sub.definingScope() as Node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,4 +37,33 @@ internal class UnusedCodeRemover: AstWalker() {
|
||||
|
||||
return removals
|
||||
}
|
||||
|
||||
|
||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(breakStmt, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(jump, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(returnStmt, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource.last() == "exit")
|
||||
reportUnreachable(functionCallStatement, parent as INameScope)
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun reportUnreachable(stmt: Statement, parent: INameScope) {
|
||||
when(val next = parent.nextSibling(stmt)) {
|
||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine, is StructDecl -> {}
|
||||
else -> errors.warn("unreachable code", next.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -724,11 +724,17 @@ reverse(array)
|
||||
|
||||
len(x)
|
||||
Number of values in the array value x, or the number of characters in a string (excluding the size or 0-byte).
|
||||
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte.
|
||||
Note: this can be different from the number of *bytes* in memory if the datatype isn't a byte. See sizeof().
|
||||
Note: lengths of strings and arrays are determined at compile-time! If your program modifies the actual
|
||||
length of the string during execution, the value of len(string) may no longer be correct!
|
||||
(use strlen function if you want to dynamically determine the length)
|
||||
|
||||
sizeof(name)
|
||||
Number of bytes that the object 'name' occupies in memory. This is a constant determined by the data type of
|
||||
the object. For instance, for a variable of type uword, the sizeof is 2.
|
||||
For an 10 element array of floats, it is 50 (on the C-64, where a float is 5 bytes).
|
||||
Note: usually you will be interested in the number of elements in an array, use len() for that.
|
||||
|
||||
strlen(str)
|
||||
Number of bytes in the string. This value is determined during runtime and counts upto
|
||||
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,101 @@
|
||||
%import c64lib
|
||||
|
||||
|
||||
;/*****************************************************************************\
|
||||
;** plasma test program for cc65. **
|
||||
;** **
|
||||
;** (w)2001 by groepaz/hitmen **
|
||||
;** **
|
||||
;** Cleanup and porting by Ullrich von Bassewitz. **
|
||||
;** Converted to prog8 by Irmen de Jong **
|
||||
;** **
|
||||
;\*****************************************************************************/
|
||||
|
||||
main {
|
||||
const uword SCREEN1 = $E000
|
||||
const uword SCREEN2 = $E400
|
||||
const uword CHARSET = $E800
|
||||
|
||||
const ubyte PAGE1 = ((SCREEN1 >> 6) & $F0) | ((CHARSET >> 10) & $0E)
|
||||
const ubyte PAGE2 = ((SCREEN2 >> 6) & $F0) | ((CHARSET >> 10) & $0E)
|
||||
|
||||
sub start() {
|
||||
c64.COLOR = 1
|
||||
c64scr.print("creating charset...\n")
|
||||
makechar()
|
||||
|
||||
ubyte block = c64.CIA2PRA
|
||||
; ubyte v = c64.VMCSB
|
||||
c64.CIA2PRA = (block & $FC) | (lsb(SCREEN1 >> 14) ^ $03)
|
||||
|
||||
repeat {
|
||||
doplasma(SCREEN1)
|
||||
c64.VMCSB = PAGE1
|
||||
doplasma(SCREEN2)
|
||||
c64.VMCSB = PAGE2
|
||||
}
|
||||
|
||||
; restore screen (if you want)
|
||||
;c64.VMCSB = v
|
||||
;c64.CIA2PRA = block
|
||||
;c64scr.print("done!\n")
|
||||
}
|
||||
|
||||
; several variables outside of doplasma to make them retain their value
|
||||
ubyte c1A
|
||||
ubyte c1B
|
||||
ubyte c2A
|
||||
ubyte c2B
|
||||
|
||||
sub doplasma(uword screen) {
|
||||
ubyte[40] xbuf
|
||||
ubyte[25] ybuf
|
||||
ubyte c1a = c1A
|
||||
ubyte c1b = c1B
|
||||
ubyte c2a = c2A
|
||||
ubyte c2b = c2B
|
||||
ubyte @zp x
|
||||
ubyte @zp y
|
||||
|
||||
for y in 0 to 24 {
|
||||
ybuf[y] = sin8u(c1a) + sin8u(c1b)
|
||||
c1a += 4
|
||||
c1b += 9
|
||||
}
|
||||
c1A += 3
|
||||
c1B -= 5
|
||||
for x in 0 to 39 {
|
||||
xbuf[x] = sin8u(c2a) + sin8u(c2b)
|
||||
c2a += 3
|
||||
c2b += 7
|
||||
}
|
||||
c2A += 2
|
||||
c2B -= 3
|
||||
for y in 0 to 24 {
|
||||
for x in 0 to 39 {
|
||||
@(screen) = xbuf[x] + ybuf[y]
|
||||
screen++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub makechar() {
|
||||
ubyte[8] bittab = [ $01, $02, $04, $08, $10, $20, $40, $80 ]
|
||||
ubyte c
|
||||
for c in 0 to 255 {
|
||||
ubyte @zp s = sin8u(c)
|
||||
ubyte i
|
||||
for i in 0 to 7 {
|
||||
ubyte b=0
|
||||
ubyte @zp ii
|
||||
for ii in 0 to 7 {
|
||||
; use 16 bit rng for a bit more randomness instead of the 8-bit rng
|
||||
if lsb(rndw()) > s {
|
||||
b |= bittab[ii]
|
||||
}
|
||||
}
|
||||
@(CHARSET + i + c*8.w) = b
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,36 +6,6 @@
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
uword count=0
|
||||
|
||||
|
||||
count=0
|
||||
repeat 10 {
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
count=0
|
||||
repeat 255 {
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
count=0
|
||||
repeat 256 {
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
count=0
|
||||
repeat 40000 {
|
||||
count++
|
||||
}
|
||||
c64scr.print_uw(count)
|
||||
c64.CHROUT('\n')
|
||||
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ turtle {
|
||||
uword xx = xpos as uword
|
||||
c64.SPXY[0] = lsb(xx) + 12
|
||||
c64.MSIGX = msb(xx) > 0
|
||||
c64.SPXY[1] = lsb(ypos) + 40
|
||||
c64.SPXY[1] = ypos as ubyte + 40
|
||||
}
|
||||
|
||||
sub pos(float x, float y) {
|
||||
@@ -66,7 +66,7 @@ turtle {
|
||||
ypos -= flen * cos(angle)
|
||||
update_turtle_sprite()
|
||||
if pendown {
|
||||
graphics.line(sx as uword, lsb(sy), xpos as uword, lsb(ypos))
|
||||
graphics.line(sx as uword, sy as ubyte, xpos as uword, ypos as ubyte)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user