mirror of
https://github.com/irmen/prog8.git
synced 2025-01-12 19:29:50 +00:00
floats from rom
This commit is contained in:
parent
9961a404ae
commit
f9617d777a
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
|
@ -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')
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user