floats from rom

This commit is contained in:
Irmen de Jong 2019-07-24 00:39:01 +02:00
parent 9961a404ae
commit f9617d777a
5 changed files with 159 additions and 107 deletions

View File

@ -3,7 +3,7 @@
<component name="NewModuleRootManager" inherit-compiler-output="true"> <component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output /> <exclude-output />
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" /> <orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component> </component>

View File

@ -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, class Block(override val name: String,

View File

@ -16,7 +16,10 @@ import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.Petscii import prog8.compiler.target.c64.Petscii
import prog8.functions.BuiltinFunctions import prog8.functions.BuiltinFunctions
import java.io.File import java.io.File
import java.math.RoundingMode
import java.util.* import java.util.*
import kotlin.math.PI
import kotlin.math.E
import kotlin.math.absoluteValue import kotlin.math.absoluteValue
@ -222,7 +225,7 @@ internal class AsmGen2(val program: Program,
} }
} }
else { 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? // it was already allocated on the zp, what to do?
// out("${variable.name} = ${zpVar.first}\t; zp ${zpVar.second}") // 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 { private fun getFloatConst(number: Double): String {
val name = globalFloatConsts[number] // try to match the ROM float constants to save memory
if(name!=null) val mflpt5 = MachineDefinition.Mflpt5.fromNumber(number)
return name val floatbytes = shortArrayOf(mflpt5.b0, mflpt5.b1, mflpt5.b2, mflpt5.b3, mflpt5.b4)
val newName = "prog8_float_const_${globalFloatConsts.size}" when {
globalFloatConsts[number] = newName floatbytes.contentEquals(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)) -> return "c64flt.FL_ZERO"
return newName 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) = 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) { 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(".") val subName = stmt.target.nameInSource.joinToString(".")
if(stmt.arglist.isNotEmpty()) { if(stmt.arglist.isNotEmpty()) {
val sub = stmt.target.targetSubroutine(program.namespace)!!
for(arg in sub.parameters.withIndex().zip(stmt.arglist)) { for(arg in sub.parameters.withIndex().zip(stmt.arglist)) {
if(arg.first.value.type!=arg.second.inferType(program)) translateSubroutineArgument(arg.first, arg.second, sub)
throw AssemblyError("argument type mismatch") }
if(sub.asmParameterRegisters.isEmpty()) { }
// pass arg via a variable out(" jsr $subName")
val paramVar = arg.first.value
val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".") if(Register.X in sub.asmClobbers)
val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position) out(" ldx c64.SCRATCH_ZPREGX") // restore X again
target.linkParents(stmt as Node) }
val literal = arg.second as? NumericLiteralValue
when { private fun translateSubroutineArgument(arg: IndexedValue<SubroutineParameter>, value: Expression, sub: Subroutine) {
literal!=null -> { val sourceDt = value.inferType(program)!!
// optimize when the argument is a constant literal if(!argumentTypeCompatible(sourceDt, arg.value.type))
when(arg.first.value.type) { throw AssemblyError("argument type incompatible")
in ByteDatatypes -> assignByteConstant(target, literal.number.toShort()) if(sub.asmParameterRegisters.isEmpty()) {
in WordDatatypes -> assignWordConstant(target, literal.number.toInt()) // pass arg via a variable
DataType.FLOAT -> assignFloatConstant(target, literal.number.toDouble()) val paramVar = arg.value
in PassByReferenceDatatypes-> TODO( "str/array/struct sub arg") val scopedParamVar = (sub.scopedname+"."+paramVar.name).split(".")
else -> throw AssemblyError("weird arg datatype") val target = AssignTarget(null, IdentifierReference(scopedParamVar, sub.position), null, null, sub.position)
} target.linkParents(value.parent)
} val literal = value as? NumericLiteralValue
arg.second is IdentifierReference -> { when {
// optimize when the argument is a variable literal!=null -> {
when (arg.first.value.type) { // optimize when the argument is a constant literal
in ByteDatatypes -> assignByteVariable(target, arg.second as IdentifierReference) when(arg.value.type) {
in WordDatatypes -> assignWordVariable(target, arg.second as IdentifierReference) in ByteDatatypes -> assignByteConstant(target, literal.number.toShort())
DataType.FLOAT -> assignFloatVariable(target, arg.second as IdentifierReference) in WordDatatypes -> assignWordConstant(target, literal.number.toInt())
in PassByReferenceDatatypes -> TODO("str/array/struct sub arg") DataType.FLOAT -> assignFloatConstant(target, literal.number.toDouble())
else -> throw AssemblyError("weird arg datatype") 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 value is IdentifierReference -> {
val paramRegister = sub.asmParameterRegisters[arg.first.index] // optimize when the argument is a variable
val statusflag = paramRegister.statusflag when (arg.value.type) {
val register = paramRegister.registerOrPair in ByteDatatypes -> assignByteVariable(target, value)
val stack = paramRegister.stack in WordDatatypes -> assignWordVariable(target, value)
when { DataType.FLOAT -> assignFloatVariable(target, value)
stack==true -> TODO("stack param") in PassByReferenceDatatypes -> TODO("str/array/struct sub arg")
statusflag!=null -> { else -> throw AssemblyError("weird arg datatype")
if (statusflag == Statusflag.Pc) TODO("carry flag param") }
else throw AssemblyError("can only use Carry as status flag parameter") }
} else -> TODO("non-constant sub arg $arg")
register!=null -> { }
val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position) } else {
target.linkParents(stmt as Node) // pass arg via a register parameter
val literal = arg.second as? NumericLiteralValue val paramRegister = sub.asmParameterRegisters[arg.index]
if(literal!=null) { val statusflag = paramRegister.statusflag
// optimize when the argument is a constant literal val register = paramRegister.registerOrPair
when(register) { val stack = paramRegister.stack
RegisterOrPair.A, when {
RegisterOrPair.X, stack==true -> TODO("param on stack")
RegisterOrPair.Y -> assignByteConstant(target, literal.number.toShort()) statusflag!=null -> {
RegisterOrPair.AX -> TODO("register A+X param $literal") if (statusflag == Statusflag.Pc) TODO("carry flag param")
RegisterOrPair.AY -> TODO("register A+Y param $literal") else throw AssemblyError("can only use Carry as status flag parameter")
RegisterOrPair.XY -> TODO("register X+Y param $literal") }
} register!=null && register.name.length==1 -> {
} else { val target = AssignTarget(Register.valueOf(register.name), null, null, null, sub.position)
TODO("register param non-const") 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) { private fun translate(stmt: Label) {

View File

@ -241,8 +241,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") || if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) { functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters // 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 val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
if(stringVar!=null) { if(stringVar!=null) {
val heapId = stringVar.heapId(program.namespace) val heapId = stringVar.heapId(program.namespace)

View File

@ -6,69 +6,51 @@
sub start() { sub start() {
float f1
; these are all floating point constants defined in the ROM so no allocation required ; 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(3.141592653589793)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = -32768.0 c64flt.print_f(-32768.0)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 1.0 c64flt.print_f( 1.0)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 0.7071067811865476 c64flt.print_f(0.7071067811865476)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 1.4142135623730951 c64flt.print_f(1.4142135623730951)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = -0.5 c64flt.print_f( -0.5)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 0.6931471805599453 c64flt.print_f(0.6931471805599453)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 10.0 c64flt.print_f(10.0)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 1.0e9 c64flt.print_f(1.0e9)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 0.5 c64flt.print_f(0.5)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 1.4426950408889634 c64flt.print_f(1.4426950408889634)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 1.5707963267948966 c64flt.print_f(1.5707963267948966)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 6.283185307179586 c64flt.print_f(6.283185307179586)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 0.25 c64flt.print_f(0.25)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
f1 = 0.0 c64flt.print_f(0.0)
c64flt.print_f(f1)
c64.CHROUT('\n') c64.CHROUT('\n')
} }
} }