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">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</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,

View File

@ -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<SubroutineParameter>, 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) {

View File

@ -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)

View File

@ -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')
}
}