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.INameScope
|
||||||
import il65.ast.Module
|
import il65.ast.Module
|
||||||
|
import java.nio.ByteBuffer
|
||||||
|
import java.nio.ByteOrder
|
||||||
import kotlin.system.exitProcess
|
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) {
|
class Compiler(val options: CompilationOptions, val namespace: INameScope) {
|
||||||
init {
|
init {
|
||||||
val zeropage = Zeropage(options)
|
val zeropage = Zeropage(options)
|
||||||
|
@ -50,13 +50,13 @@ class Zeropage(private val options: CompilationOptions) {
|
|||||||
DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).intvalue!!
|
DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).intvalue!!
|
||||||
DataType.WORD -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * 2
|
DataType.WORD -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * 2
|
||||||
DataType.FLOAT -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * 5
|
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 {
|
} else {
|
||||||
// 2 dimensional matrix (only bytes for now)
|
// 2 dimensional matrix (only bytes for now)
|
||||||
when(vardecl.datatype) {
|
when(vardecl.datatype) {
|
||||||
DataType.BYTE -> (vardecl.arrayspec.x as LiteralValue).intvalue!! * y
|
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 {
|
} else {
|
||||||
@ -67,9 +67,9 @@ class Zeropage(private val options: CompilationOptions) {
|
|||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
println("${vardecl.position} warning: allocating a large value in zeropage")
|
println("${vardecl.position} warning: allocating a large value in zeropage")
|
||||||
5
|
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 {
|
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.VarDecl
|
||||||
import il65.ast.VarDeclType
|
import il65.ast.VarDeclType
|
||||||
import il65.compiler.*
|
import il65.compiler.*
|
||||||
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
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)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestZeropage {
|
class TestZeropage {
|
||||||
@Test
|
@Test
|
||||||
@ -30,7 +97,7 @@ class TestZeropage {
|
|||||||
@Test
|
@Test
|
||||||
fun testZpFloatEnable() {
|
fun testZpFloatEnable() {
|
||||||
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, false))
|
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, false))
|
||||||
assertFailsWith<UnsupportedOperationException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
||||||
}
|
}
|
||||||
val zp2 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
|
val zp2 = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, true))
|
||||||
@ -41,7 +108,7 @@ class TestZeropage {
|
|||||||
fun testCompatibleAllocation() {
|
fun testCompatibleAllocation() {
|
||||||
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.COMPATIBLE, true))
|
val zp = Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.COMPATIBLE, true))
|
||||||
assert(zp.available() == 9)
|
assert(zp.available() == 9)
|
||||||
assertFailsWith<UnsupportedOperationException> {
|
assertFailsWith<CompilerException> {
|
||||||
// in regular zp there aren't 5 sequential bytes free
|
// in regular zp there aren't 5 sequential bytes free
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
||||||
}
|
}
|
||||||
@ -50,10 +117,10 @@ class TestZeropage {
|
|||||||
assert(loc > 0)
|
assert(loc > 0)
|
||||||
}
|
}
|
||||||
assert(zp.available() == 0)
|
assert(zp.available() == 0)
|
||||||
assertFailsWith<UnsupportedOperationException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
||||||
}
|
}
|
||||||
assertFailsWith<UnsupportedOperationException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.WORD, null, "", null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,7 +140,7 @@ class TestZeropage {
|
|||||||
}
|
}
|
||||||
assert(zp.available() == 19)
|
assert(zp.available() == 19)
|
||||||
|
|
||||||
assertFailsWith<UnsupportedOperationException> {
|
assertFailsWith<CompilerException> {
|
||||||
// can't allocate because no more sequential bytes, only fragmented
|
// can't allocate because no more sequential bytes, only fragmented
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.FLOAT, null, "", null))
|
||||||
}
|
}
|
||||||
@ -86,7 +153,7 @@ class TestZeropage {
|
|||||||
|
|
||||||
assert(zp.available() == 1)
|
assert(zp.available() == 1)
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
||||||
assertFailsWith<UnsupportedOperationException> {
|
assertFailsWith<CompilerException> {
|
||||||
// no more space
|
// no more space
|
||||||
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
zp.allocate(VarDecl(VarDeclType.VAR, DataType.BYTE, null, "", null))
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user