diff --git a/DeprecatedStackVm/DeprecatedStackVm.iml b/DeprecatedStackVm/DeprecatedStackVm.iml
index 591676c66..768559d7e 100644
--- a/DeprecatedStackVm/DeprecatedStackVm.iml
+++ b/DeprecatedStackVm/DeprecatedStackVm.iml
@@ -3,7 +3,7 @@
-
+
diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt
index 9e0191504..ff530d93a 100644
--- a/compiler/src/prog8/ast/statements/AstStatements.kt
+++ b/compiler/src/prog8/ast/statements/AstStatements.kt
@@ -49,7 +49,7 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio
}
-data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean?)
+data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
class Block(override val name: String,
diff --git a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt
index fe039c9be..3507624f4 100644
--- a/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt
+++ b/compiler/src/prog8/compiler/target/c64/codegen2/AsmGen2.kt
@@ -16,7 +16,10 @@ import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions
import java.io.File
+import java.math.RoundingMode
import java.util.*
+import kotlin.math.PI
+import kotlin.math.E
import kotlin.math.absoluteValue
@@ -222,7 +225,7 @@ internal class AsmGen2(val program: Program,
}
}
else {
- TODO("already allocated on zp?? $zpVar")
+ throw AssemblyError("huh, var is already on zp $zpVar")
// it was already allocated on the zp, what to do?
// out("${variable.name} = ${zpVar.first}\t; zp ${zpVar.second}")
}
@@ -373,12 +376,44 @@ internal class AsmGen2(val program: Program,
}
private fun getFloatConst(number: Double): String {
- val name = globalFloatConsts[number]
- if(name!=null)
- return name
- val newName = "prog8_float_const_${globalFloatConsts.size}"
- globalFloatConsts[number] = newName
- return newName
+ // try to match the ROM float constants to save memory
+ val mflpt5 = MachineDefinition.Mflpt5.fromNumber(number)
+ val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
+ when {
+ floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_ZERO"
+ floatbytes.contentEquals(shortArrayOf(0x82, 0x49, 0x0f, 0xda, 0xa1)) -> return "c64flt.FL_PIVAL"
+ floatbytes.contentEquals(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_N32768"
+ floatbytes.contentEquals(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FONE"
+ floatbytes.contentEquals(shortArrayOf(0x80, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRHLF"
+ floatbytes.contentEquals(shortArrayOf(0x81, 0x35, 0x04, 0xf3, 0x34)) -> return "c64flt.FL_SQRTWO"
+ floatbytes.contentEquals(shortArrayOf(0x80, 0x80, 0x00, 0x00, 0x00)) -> return "c64flt.FL_NEGHLF"
+ floatbytes.contentEquals(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xf8)) -> return "c64flt.FL_LOG2"
+ floatbytes.contentEquals(shortArrayOf(0x84, 0x20, 0x00, 0x00, 0x00)) -> return "c64flt.FL_TENC"
+ floatbytes.contentEquals(shortArrayOf(0x9e, 0x6e, 0x6b, 0x28, 0x00)) -> return "c64flt.FL_NZMIL"
+ floatbytes.contentEquals(shortArrayOf(0x80, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FHALF"
+ floatbytes.contentEquals(shortArrayOf(0x81, 0x38, 0xaa, 0x3b, 0x29)) -> return "c64flt.FL_LOGEB2"
+ floatbytes.contentEquals(shortArrayOf(0x81, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_PIHALF"
+ floatbytes.contentEquals(shortArrayOf(0x83, 0x49, 0x0f, 0xda, 0xa2)) -> return "c64flt.FL_TWOPI"
+ floatbytes.contentEquals(shortArrayOf(0x7f, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_FR4"
+ else -> {
+ // attempt to correct for a few rounding issues
+ when (number.toBigDecimal().setScale(10, RoundingMode.HALF_DOWN).toDouble()) {
+ 3.1415926536 -> return "c64flt.FL_PIVAL"
+ 1.4142135624 -> return "c64flt.FL_SQRTWO"
+ 0.7071067812 -> return "c64flt.FL_SQRHLF"
+ 0.6931471806 -> return "c64flt.FL_LOG2"
+ else -> {}
+ }
+
+ // no ROM float const for this value, create our own
+ val name = globalFloatConsts[number]
+ if(name!=null)
+ return name
+ val newName = "prog8_float_const_${globalFloatConsts.size}"
+ globalFloatConsts[number] = newName
+ return newName
+ }
+ }
}
private fun signExtendAtoMsb(destination: String) =
@@ -466,78 +501,115 @@ internal class AsmGen2(val program: Program,
}
}
+ private fun argumentTypeCompatible(argType: DataType, paramType: DataType): Boolean {
+ if(argType isAssignableTo paramType)
+ return true
+
+ // we have a special rule for some types.
+ // strings are assignable to UWORD, for example, and vice versa
+ if(argType in StringDatatypes && paramType==DataType.UWORD)
+ return true
+ if(argType==DataType.UWORD && paramType in StringDatatypes)
+ return true
+
+ return false
+ }
+
private fun translateSubroutineCall(stmt: IFunctionCall) {
+ val sub = stmt.target.targetSubroutine(program.namespace)!!
+ if(Register.X in sub.asmClobbers)
+ out(" stx c64.SCRATCH_ZPREGX") // we only save X for now (required! is the eval stack pointer), screw A and Y...
+
val subName = stmt.target.nameInSource.joinToString(".")
if(stmt.arglist.isNotEmpty()) {
- val sub = stmt.target.targetSubroutine(program.namespace)!!
for(arg in sub.parameters.withIndex().zip(stmt.arglist)) {
- if(arg.first.value.type!=arg.second.inferType(program))
- throw AssemblyError("argument type mismatch")
- if(sub.asmParameterRegisters.isEmpty()) {
- // pass arg via a variable
- val paramVar = arg.first.value
- val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
- val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
- target.linkParents(stmt as Node)
- val literal = arg.second as? NumericLiteralValue
- when {
- literal!=null -> {
- // optimize when the argument is a constant literal
- when(arg.first.value.type) {
- in ByteDatatypes -> assignByteConstant(target, literal.number.toShort())
- in WordDatatypes -> assignWordConstant(target, literal.number.toInt())
- DataType.FLOAT -> assignFloatConstant(target, literal.number.toDouble())
- in PassByReferenceDatatypes-> TODO( "str/array/struct sub arg")
- else -> throw AssemblyError("weird arg datatype")
- }
- }
- arg.second is IdentifierReference -> {
- // optimize when the argument is a variable
- when (arg.first.value.type) {
- in ByteDatatypes -> assignByteVariable(target, arg.second as IdentifierReference)
- in WordDatatypes -> assignWordVariable(target, arg.second as IdentifierReference)
- DataType.FLOAT -> assignFloatVariable(target, arg.second as IdentifierReference)
- in PassByReferenceDatatypes -> TODO("str/array/struct sub arg")
- else -> throw AssemblyError("weird arg datatype")
- }
- }
- else -> TODO("non-constant sub arg $arg")
+ translateSubroutineArgument(arg.first, arg.second, sub)
+ }
+ }
+ out(" jsr $subName")
+
+ if(Register.X in sub.asmClobbers)
+ out(" ldx c64.SCRATCH_ZPREGX") // restore X again
+ }
+
+ private fun translateSubroutineArgument(arg: IndexedValue, value: Expression, sub: Subroutine) {
+ val sourceDt = value.inferType(program)!!
+ if(!argumentTypeCompatible(sourceDt, arg.value.type))
+ throw AssemblyError("argument type incompatible")
+ if(sub.asmParameterRegisters.isEmpty()) {
+ // pass arg via a variable
+ val paramVar = arg.value
+ val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
+ val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
+ target.linkParents(value.parent)
+ val literal = value as? NumericLiteralValue
+ when {
+ literal!=null -> {
+ // optimize when the argument is a constant literal
+ when(arg.value.type) {
+ in ByteDatatypes -> assignByteConstant(target, literal.number.toShort())
+ in WordDatatypes -> assignWordConstant(target, literal.number.toInt())
+ DataType.FLOAT -> assignFloatConstant(target, literal.number.toDouble())
+ in PassByReferenceDatatypes-> TODO( "str/array/struct sub arg")
+ else -> throw AssemblyError("weird arg datatype")
}
- } else {
- // pass arg via a register parameter
- val paramRegister = sub.asmParameterRegisters[arg.first.index]
- val statusflag = paramRegister.statusflag
- val register = paramRegister.registerOrPair
- val stack = paramRegister.stack
- when {
- stack==true -> TODO("stack param")
- statusflag!=null -> {
- if (statusflag == Statusflag.Pc) TODO("carry flag param")
- else throw AssemblyError("can only use Carry as status flag parameter")
- }
- register!=null -> {
- val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
- target.linkParents(stmt as Node)
- val literal = arg.second as? NumericLiteralValue
- if(literal!=null) {
- // optimize when the argument is a constant literal
- when(register) {
- RegisterOrPair.A,
- RegisterOrPair.X,
- RegisterOrPair.Y -> assignByteConstant(target, literal.number.toShort())
- RegisterOrPair.AX -> TODO("register A+X param $literal")
- RegisterOrPair.AY -> TODO("register A+Y param $literal")
- RegisterOrPair.XY -> TODO("register X+Y param $literal")
- }
- } else {
- TODO("register param non-const")
- }
- }
+ }
+ value is IdentifierReference -> {
+ // optimize when the argument is a variable
+ when (arg.value.type) {
+ in ByteDatatypes -> assignByteVariable(target, value)
+ in WordDatatypes -> assignWordVariable(target, value)
+ DataType.FLOAT -> assignFloatVariable(target, value)
+ in PassByReferenceDatatypes -> TODO("str/array/struct sub arg")
+ else -> throw AssemblyError("weird arg datatype")
+ }
+ }
+ else -> TODO("non-constant sub arg $arg")
+ }
+ } else {
+ // pass arg via a register parameter
+ val paramRegister = sub.asmParameterRegisters[arg.index]
+ val statusflag = paramRegister.statusflag
+ val register = paramRegister.registerOrPair
+ val stack = paramRegister.stack
+ when {
+ stack==true -> TODO("param on stack")
+ statusflag!=null -> {
+ if (statusflag == Statusflag.Pc) TODO("carry flag param")
+ else throw AssemblyError("can only use Carry as status flag parameter")
+ }
+ register!=null && register.name.length==1 -> {
+ val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
+ target.linkParents(value.parent)
+ val literal = value as? NumericLiteralValue
+ if(literal!=null) {
+ // optimize when the argument is a constant literal
+ assignByteConstant(target, literal.number.toShort())
+ } else {
+ TODO("single register param non-const")
+ }
+ }
+ register!=null && register.name.length==2 -> {
+ // register pair as a 16-bit value (only possible for subroutine parameters)
+ val literal = value as? NumericLiteralValue
+ if(literal!=null) {
+ // optimize when the argument is a constant literal
+ val hex = literal.number.toHex()
+ if (register == RegisterOrPair.AX) out(" lda #<$hex | ldx #>$hex")
+ else if (register == RegisterOrPair.AY) out(" lda #<$hex | ldy #>$hex")
+ else if (register == RegisterOrPair.XY) out(" ldx #<$hex | ldy #>$hex")
+ } else if(value is AddressOf) {
+ // optimize when the argument is an address of something
+ val sourceName = value.identifier.nameInSource.joinToString(".")
+ if (register == RegisterOrPair.AX) out(" lda #<$sourceName | ldx #>$sourceName")
+ else if (register == RegisterOrPair.AY) out(" lda #<$sourceName | ldy #>$sourceName")
+ else if (register == RegisterOrPair.XY) out(" ldx #<$sourceName | ldy #>$sourceName")
+ } else {
+ TODO("register pair param non-const $register = ${value}")
}
}
}
}
- out(" jsr $subName")
}
private fun translate(stmt: Label) {
diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt
index ce1929bb4..2f0fee16f 100644
--- a/compiler/src/prog8/optimizer/StatementOptimizer.kt
+++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt
@@ -241,8 +241,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
- if(functionCallStatement.arglist.single() is NumericLiteralValue)
- throw AstException("string argument should be on heap already")
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
if(stringVar!=null) {
val heapId = stringVar.heapId(program.namespace)
diff --git a/examples/romfloats.p8 b/examples/romfloats.p8
index ce06041f5..15429528b 100644
--- a/examples/romfloats.p8
+++ b/examples/romfloats.p8
@@ -6,69 +6,51 @@
sub start() {
- float f1
-
; these are all floating point constants defined in the ROM so no allocation required
- ; TODO actually read these from ROM
- f1 = 3.141592653589793
- c64flt.print_f(f1)
+ c64flt.print_f(3.141592653589793)
c64.CHROUT('\n')
- f1 = -32768.0
- c64flt.print_f(f1)
+ c64flt.print_f(-32768.0)
c64.CHROUT('\n')
- f1 = 1.0
- c64flt.print_f(f1)
+ c64flt.print_f( 1.0)
c64.CHROUT('\n')
- f1 = 0.7071067811865476
- c64flt.print_f(f1)
+ c64flt.print_f(0.7071067811865476)
c64.CHROUT('\n')
- f1 = 1.4142135623730951
- c64flt.print_f(f1)
+ c64flt.print_f(1.4142135623730951)
c64.CHROUT('\n')
- f1 = -0.5
- c64flt.print_f(f1)
+ c64flt.print_f( -0.5)
c64.CHROUT('\n')
- f1 = 0.6931471805599453
- c64flt.print_f(f1)
+ c64flt.print_f(0.6931471805599453)
c64.CHROUT('\n')
- f1 = 10.0
- c64flt.print_f(f1)
+ c64flt.print_f(10.0)
c64.CHROUT('\n')
- f1 = 1.0e9
- c64flt.print_f(f1)
+ c64flt.print_f(1.0e9)
c64.CHROUT('\n')
- f1 = 0.5
- c64flt.print_f(f1)
+ c64flt.print_f(0.5)
c64.CHROUT('\n')
- f1 = 1.4426950408889634
- c64flt.print_f(f1)
+ c64flt.print_f(1.4426950408889634)
c64.CHROUT('\n')
- f1 = 1.5707963267948966
- c64flt.print_f(f1)
+ c64flt.print_f(1.5707963267948966)
c64.CHROUT('\n')
- f1 = 6.283185307179586
- c64flt.print_f(f1)
+ c64flt.print_f(6.283185307179586)
c64.CHROUT('\n')
- f1 = 0.25
- c64flt.print_f(f1)
+ c64flt.print_f(0.25)
c64.CHROUT('\n')
- f1 = 0.0
- c64flt.print_f(f1)
+ c64flt.print_f(0.0)
c64.CHROUT('\n')
}
}