mflpt5 but not correct yet

This commit is contained in:
Irmen de Jong 2018-08-17 01:47:07 +02:00
parent 26a7a3c8d0
commit 8368633ed2
3 changed files with 184 additions and 11 deletions

View File

@ -2,9 +2,115 @@ package il65.compiler
import il65.ast.INameScope
import il65.ast.Module
import java.nio.ByteBuffer
import java.nio.ByteOrder
import kotlin.system.exitProcess
class CompilerException(message: String?) : Exception(message)
// 5-byte cbm MFLPT format limitations:
const val FLOAT_MAX_POSITIVE = 1.7014118345e+38
const val FLOAT_MAX_NEGATIVE = -1.7014118345e+38
fun Number.toHex(): String {
// 0..15 -> "0".."15"
// 16..255 -> "$10".."$ff"
// 256..65536 -> "$0100".."$ffff"
val integer = this.toInt()
return when (integer) {
in 0 until 16 -> integer.toString()
in 0 until 0x100 -> "$"+integer.toString(16).padStart(2,'0')
in 0 until 0x10000 -> "$"+integer.toString(16).padStart(4,'0')
else -> throw CompilerException("number too large for 16 bits $this")
}
}
fun Double.frexp(): Pair<Double, Int> {
var exponent: Int = Math.getExponent(this)
val mantissa: Double
when (exponent) {
1024
-> {
// Inf or NaN
mantissa = this
exponent = 0
}
-1023 -> if (this == 0.0) {
// -0.0 or 0.0
mantissa = this
exponent = 0
} else {
exponent = Math.getExponent(this * 0x10000000000000) - 51 // that is 0x1p52 == 2**52
mantissa = Math.scalb(this, -exponent)
}
else -> {
exponent++
mantissa = Math.scalb(this, -exponent)
}
}
return Pair(mantissa, exponent)
}
fun Number.toMflpt5(): ShortArray {
// algorithm here https://sourceforge.net/p/acme-crossass/code-0/62/tree/trunk/ACME_Lib/cbm/mflpt.a
// @todo fix this
var flt = this.toDouble()
if(flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
throw CompilerException("floating point number out of 5-byte mflpt range: $this")
if(flt==0.0)
return shortArrayOf(0, 0, 0, 0, 0)
val sign: Long =
when {
flt < 0.0 -> {
flt = -flt
0x80000000L
}
else -> 0x00000000L
}
var (mant, exp) = flt.frexp()
exp += 128
if(exp < 1) {
// underflow, use zero instead
return shortArrayOf(0, 0, 0 ,0 ,0)
}
if(exp > 255) {
throw CompilerException("floating point number out of 5-byte mflpt range: $this")
}
val mflpt = sign.or((mant * 0x100000000L).toLong()).and(0x7fffffffL)
val result = ShortArray(5)
result[0] = exp.toShort()
result[1] = (mflpt and -0x1000000 shr 24).toShort()
result[2] = (mflpt and 0x00FF0000 shr 16).toShort()
result[3] = (mflpt and 0x0000FF00 shr 8).toShort()
result[4] = (mflpt and 0x000000FF shr 0).toShort()
return result
/*
mant, exp = math.frexp(number)
exp += 128
if exp < 1:
# underflow, use zero instead
return bytearray([0, 0, 0, 0, 0])
if exp > 255:
raise OverflowError("floating point number out of 5-byte mflpt range", number)
mant = sign | int(mant * 0x100000000) & 0x7fffffff
return bytearray([exp]) + int.to_bytes(mant, 4, "big")
*/
}
class Compiler(val options: CompilationOptions, val namespace: INameScope) {
init {
val zeropage = Zeropage(options)

View File

@ -50,13 +50,13 @@ class Zeropage(private val options: CompilationOptions) {
DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).intvalue!!
DataType.WORD -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * 2
DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * 5
else -> throw UnsupportedOperationException("array can only be of byte, word, float")
else -> throw CompilerException("array can only be of byte, word, float")
}
} else {
// 2 dimensional matrix (only bytes for now)
when(vardecl.datatype) {
DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * y
else -> throw UnsupportedOperationException("matrix can only be of byte")
else -> throw CompilerException("matrix can only be of byte")
}
}
} else {
@ -67,9 +67,9 @@ class Zeropage(private val options: CompilationOptions) {
if (options.floats) {
println("${vardecl.position} warning: allocating a large value in zeropage")
5
} else throw UnsupportedOperationException("floating point option not enabled")
} else throw CompilerException("floating point option not enabled")
}
else -> throw UnsupportedOperationException("cannot put datatype ${vardecl.datatype} in zeropage")
else -> throw CompilerException("cannot put datatype ${vardecl.datatype} in zeropage")
}
}
@ -87,7 +87,7 @@ class Zeropage(private val options: CompilationOptions) {
}
}
throw UnsupportedOperationException("ERROR: no free space in ZP to allocate $size sequential bytes")
throw CompilerException("ERROR: no free space in ZP to allocate $size sequential bytes")
}
private fun makeAllocation(location: Int, size: Int, datatype: DataType, name: String?): Int {

View File

@ -4,10 +4,77 @@ import il65.ast.DataType
import il65.ast.VarDecl
import il65.ast.VarDeclType
import il65.compiler.*
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestCompiler {
@Test
fun testToHex() {
assertEquals("0", 0.toHex())
assertEquals("1", 1.toHex())
assertEquals("1", 1.234.toHex())
assertEquals("10", 10.toHex())
assertEquals("10", 10.99.toHex())
assertEquals("15", 15.toHex())
assertEquals("$10", 16.toHex())
assertEquals("\$ff", 255.toHex())
assertEquals("$0100", 256.toHex())
assertEquals("$4e5c", 20060.toHex())
assertEquals("\$ffff", 65535.toHex())
assertEquals("\$ffff", 65535L.toHex())
assertFailsWith<CompilerException> { 65536.toHex() }
assertFailsWith<CompilerException> { (-1).toHex() }
assertFailsWith<CompilerException> { (-1.99).toHex() }
}
@Test
fun testFloatToMflpt5() {
assertThat((0).toMflpt5(), equalTo(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)))
assertThat((3.141592653).toMflpt5(), equalTo(shortArrayOf(0x82, 0x49, 0x0F, 0xDA, 0xA1)))
assertThat((3.141592653589793).toMflpt5(), equalTo(shortArrayOf(0x82, 0x49, 0x0F, 0xDA, 0xA2)))
assertThat((-32768).toMflpt5(), equalTo(shortArrayOf(0x90, 0x80, 0x00, 0x00, 0x00)))
assertThat((1).toMflpt5(), equalTo(shortArrayOf(0x81, 0x00, 0x00, 0x00, 0x00)))
assertThat((0.7071067812).toMflpt5(), equalTo(shortArrayOf(0x80, 0x35, 0x04, 0xF3, 0x34)))
assertThat((0.7071067811865476).toMflpt5(), equalTo(shortArrayOf(0x80, 0x35, 0x04, 0xF3, 0x33)))
assertThat((1.4142135624).toMflpt5(), equalTo(shortArrayOf(0x81, 0x35, 0x04, 0xF3, 0x34)))
assertThat((1.4142135623730951).toMflpt5(), equalTo(shortArrayOf(0x81, 0x35, 0x04, 0xF3, 0x33)))
assertThat((-.5).toMflpt5(), equalTo(shortArrayOf(0x80, 0x80, 0x00, 0x00, 0x00)))
assertThat((0.69314718061).toMflpt5(), equalTo(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xF8)))
assertThat((0.6931471805599453).toMflpt5(), equalTo(shortArrayOf(0x80, 0x31, 0x72, 0x17, 0xF7)))
assertThat((10).toMflpt5(), equalTo(shortArrayOf(0x84, 0x20, 0x00, 0x00, 0x00)))
assertThat((1000000000).toMflpt5(), equalTo(shortArrayOf(0x9E, 0x6E, 0x6B, 0x28, 0x00)))
assertThat((.5).toMflpt5(), equalTo(shortArrayOf(0x80, 0x00, 0x00, 0x00, 0x00)))
assertThat((1.4426950408889634).toMflpt5(), equalTo(shortArrayOf(0x81, 0x38, 0xAA, 0x3B, 0x29)))
assertThat((1.5707963267948966).toMflpt5(), equalTo(shortArrayOf(0x81, 0x49, 0x0F, 0xDA, 0xA2)))
assertThat((6.283185307179586).toMflpt5(), equalTo(shortArrayOf(0x83, 0x49, 0x0F, 0xDA, 0xA2)))
assertThat((.25).toMflpt5(), equalTo(shortArrayOf(0x7F, 0x00, 0x00, 0x00, 0x00)))
}
@Test
fun testFloatRange() {
assertThat(FLOAT_MAX_POSITIVE.toMflpt5(), equalTo(shortArrayOf(0xff, 0x7f, 0xff, 0xff, 0xff)))
assertThat(FLOAT_MAX_NEGATIVE.toMflpt5(), equalTo(shortArrayOf(0xff, 0xff, 0xff, 0xff, 0xff)))
assertThat((1.7e-38).toMflpt5(), equalTo(shortArrayOf(0x03, 0x39, 0x1d, 0x15, 0x63)))
assertThat((1.7e-39).toMflpt5(), equalTo(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)))
assertThat((-1.7e-38).toMflpt5(), equalTo(shortArrayOf(0x03, 0xb9, 0x1d, 0x15, 0x63)))
assertThat((-1.7e-39).toMflpt5(), equalTo(shortArrayOf(0x00, 0x00, 0x00, 0x00, 0x00)))
assertFailsWith<CompilerException> { 1.7014118346e+38.toMflpt5() }
assertFailsWith<CompilerException> { (-1.7014118346e+38).toMflpt5() }
assertFailsWith<CompilerException> { 1.7014118347e+38.toMflpt5() }
assertFailsWith<CompilerException> { (-1.7014118347e+38).toMflpt5() }
}
// @todo test the other way round, mflpt-bytes -> float.
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestZeropage {
@Test
@ -30,7 +97,7 @@ class TestZeropage {
@Test
fun testZpFloatEnable() {
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, false))
assertFailsWith<UnsupportedOperationException> {
assertFailsWith<CompilerException> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
}
val zp2 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
@ -41,7 +108,7 @@ class TestZeropage {
fun testCompatibleAllocation() {
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.COMPATIBLE, true))
assert(zp.available() == 9)
assertFailsWith<UnsupportedOperationException> {
assertFailsWith<CompilerException> {
// in regular zp there aren't 5 sequential bytes free
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
}
@ -50,10 +117,10 @@ class TestZeropage {
assert(loc > 0)
}
assert(zp.available() == 0)
assertFailsWith<UnsupportedOperationException> {
assertFailsWith<CompilerException> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
}
assertFailsWith<UnsupportedOperationException> {
assertFailsWith<CompilerException> {
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
}
}
@ -73,7 +140,7 @@ class TestZeropage {
}
assert(zp.available() == 19)
assertFailsWith<UnsupportedOperationException> {
assertFailsWith<CompilerException> {
// can't allocate because no more sequential bytes, only fragmented
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
}
@ -86,7 +153,7 @@ class TestZeropage {
assert(zp.available() == 1)
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
assertFailsWith<UnsupportedOperationException> {
assertFailsWith<CompilerException> {
// no more space
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
}