mirror of
https://github.com/irmen/prog8.git
synced 2025-02-28 09:29:26 +00:00
mflpt5 but not correct yet
This commit is contained in:
parent
26a7a3c8d0
commit
8368633ed2
@ -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)
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user