fix type casting issues and unary ^ operator

signed numbers are no longer implicitly converted to unsigned
proper range check on bankof()
This commit is contained in:
Irmen de Jong 2024-12-01 14:28:41 +01:00
parent 58f696d00a
commit 50c3d809dc
18 changed files with 271 additions and 86 deletions

View File

@ -294,6 +294,14 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
if (trunc != number)
throw IllegalArgumentException("refused truncating of float to avoid loss of precision @$position")
}
when(type) {
DataType.UBYTE -> require(number in 0.0..255.0)
DataType.BYTE -> require(number in -128.0..127.0)
DataType.UWORD -> require(number in 0.0..65535.0)
DataType.WORD -> require(number in -32728.0..32767.0)
DataType.LONG -> require(number in -2147483647.0..2147483647.0)
else -> {}
}
}
override fun hashCode(): Int = Objects.hash(type, number)
@ -318,7 +326,7 @@ class PtPrefix(val operator: String, type: DataType, position: Position): PtExpr
get() = children.single() as PtExpression
init {
require(operator in setOf("+", "-", "~", "^", "<<", "not")) { "invalid prefix operator: $operator" }
require(operator in setOf("+", "-", "~", "^", "<<", "not")) { "invalid prefix operator: $operator" } // TODO ^ and << are experimental
}
}

View File

@ -4,7 +4,7 @@ val AssociativeOperators = setOf("+", "*", "&", "|", "^", "==", "!=", "xor")
val ComparisonOperators = setOf("==", "!=", "<", ">", "<=", ">=")
val LogicalOperators = setOf("and", "or", "xor", "not", "in")
val BitwiseOperators = setOf("&", "|", "^", "~")
val PrefixOperators = setOf("+", "-", "~", "not")
val PrefixOperators = setOf("+", "-", "~", "^", "<<", "not") // TODO ^ and << are experimental
fun invertedComparisonOperator(operator: String) =
when (operator) {

View File

@ -1089,7 +1089,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})
@ -1135,7 +1135,7 @@ object PetsciiEncoding {
Ok(text.map {
try {
encodeChar(it, lowercase)
} catch (x: CharConversionException) {
} catch (_: CharConversionException) {
encodeChar(it, !lowercase)
}
})

View File

@ -275,7 +275,7 @@ private fun compileMain(args: Array<String>): Boolean {
return false
else
compilationResult = result
} catch (x: AstException) {
} catch (_: AstException) {
return false
}

View File

@ -23,7 +23,7 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"lsw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 65535).toDouble() } },
"msb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 8 and 255).toDouble()} },
"msw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 16 and 65535).toDouble()} },
"bankof" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x ushr 16 and 255).toDouble()} },
"bankof" to ::builtinBankof,
"mkword" to ::builtinMkword,
"clamp__ubyte" to ::builtinClampUByte,
"clamp__byte" to ::builtinClampByte,
@ -153,6 +153,15 @@ private fun builtinLen(args: List<Expression>, position: Position, program: Prog
}
}
private fun builtinBankof(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 1)
throw SyntaxError("bankof requires one argument", position)
val const = args[0].constValue(program)?.number?.toInt() ?: throw NotConstArgumentException()
if(const > 0xffffff)
throw SyntaxError("integer overflow, bank exceeds 255", position)
return NumericLiteral(DataType.UBYTE, (const ushr 16 and 255).toDouble(), position)
}
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteral {
if (args.size != 2)
throw SyntaxError("mkword requires msb and lsb arguments", position)

View File

@ -1142,21 +1142,33 @@ internal class AstChecker(private val program: Program,
if(dt==DataType.UNDEFINED)
return // any error should be reported elsewhere
if(expr.operator=="-") {
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
errors.err("can only take negative of a signed number type", expr.position)
when (expr.operator) {
"-" -> {
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
errors.err("can only take negative of a signed number type", expr.position)
}
}
}
else if(expr.operator == "~") {
if(dt !in IntegerDatatypes)
errors.err("can only use bitwise invert on integer types", expr.position)
else if(dt==DataType.BOOL)
errors.err("bitwise invert is for integer types, use 'not' on booleans", expr.position)
}
else if(expr.operator == "not") {
if(dt!=DataType.BOOL) {
errors.err("logical not is for booleans", expr.position)
"~" -> {
if(dt !in IntegerDatatypes)
errors.err("can only use bitwise invert on integer types", expr.position)
else if(dt==DataType.BOOL)
errors.err("bitwise invert is for integer types, use 'not' on booleans", expr.position)
}
"not" -> {
if(dt!=DataType.BOOL) {
errors.err("logical not is for booleans", expr.position)
}
}
"^" -> { // TODO ^ prefix operator is experimental
if(dt in IntegerDatatypes) {
val value = expr.expression.constValue(program)?.number?.toInt()
if(value!=null && value > 0xffffff) {
errors.err("integer overflow, bank exceeds 255", expr.position)
}
} else
errors.err("bankof operator can only take integer values", expr.position)
}
"<<" -> throw FatalAstException("unary << should have been replaced by a const uword") // TODO << is experimental
}
super.visit(expr)
}

View File

@ -85,7 +85,7 @@ class AstPreprocessor(val program: Program,
val constval = range.to.constValue(program)
if(constval!=null)
modifications += IAstModification.ReplaceNode(range.to, constval, range)
} catch (x: SyntaxError) {
} catch (_: SyntaxError) {
// syntax errors will be reported later
}
}
@ -94,7 +94,7 @@ class AstPreprocessor(val program: Program,
val constval = range.step.constValue(program)
if(constval!=null)
modifications += IAstModification.ReplaceNode(range.step, constval, range)
} catch (x: SyntaxError) {
} catch (_: SyntaxError) {
// syntax errors will be reported later
}
}

View File

@ -266,7 +266,7 @@ class TestCompilerOnRanges: FunSpec({
(array as ArrayLiteral).value.size shouldBe 26
val forloop = (statements.dropLast(1).last() as ForLoop)
forloop.iterable shouldBe instanceOf<RangeExpression>()
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.UBYTE, -2.0, Position.DUMMY)
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.BYTE, -2.0, Position.DUMMY)
}
test("range with start/end variables should be ok") {
@ -285,7 +285,7 @@ class TestCompilerOnRanges: FunSpec({
val statements = result.compilerAst.entrypoint.statements
val forloop = (statements.dropLast(1).last() as ForLoop)
forloop.iterable shouldBe instanceOf<RangeExpression>()
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.UBYTE, -2.0, Position.DUMMY)
(forloop.iterable as RangeExpression).step shouldBe NumericLiteral(DataType.BYTE, -2.0, Position.DUMMY)
}

View File

@ -6,11 +6,7 @@ import io.kotest.matchers.doubles.plusOrMinus
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Assignment
import prog8.code.core.DataType
import prog8.code.core.InternalCompilerException
import prog8.code.core.Position
import prog8.code.core.toHex
import prog8.code.target.C64Target
import prog8.code.target.cbm.Mflpt5
@ -162,15 +158,33 @@ class TestNumbers: FunSpec({
sub start() {
uword @shared qq = ${'$'}2ff33
cx16.r0 = ${'$'}1fc0f
cx16.r0L = 1234
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 2
errors.errors.size shouldBe 6
errors.warnings.size shouldBe 0
errors.errors[0] shouldContain "out of range"
errors.errors[1] shouldContain "out of range"
errors.errors[1] shouldContain "doesn't match"
errors.errors[2] shouldContain "out of range"
errors.errors[3] shouldContain "doesn't match"
errors.errors[4] shouldContain "out of range"
errors.errors[5] shouldContain "cannot assign word to byte"
}
test("large numeric literals still ok if actual value is small") {
val src="""
main {
sub start() {
cx16.r1L = %000000000001
cx16.r2L = ${'$'}000000000001
}
}
"""
val errors = ErrorReporterForTests()
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldNotBe null
}
test("big numbers okay in const expressions if result fits") {
@ -185,7 +199,7 @@ class TestNumbers: FunSpec({
compileText(C64Target(), true, src, writeAssembly = false) shouldNotBe null
}
test("signed negative numbers cast to unsigned allowed") {
test("signed negative numbers not implicitly cast to unsigned") {
val src="""
main {
sub start() {
@ -197,13 +211,14 @@ class TestNumbers: FunSpec({
}
}
"""
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
val statements = result.compilerAst.entrypoint.statements
statements.size shouldBe 8
(statements[1] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 32768.0, Position.DUMMY)
(statements[3] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65535.0, Position.DUMMY)
(statements[5] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
(statements[6] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65534.0, Position.DUMMY)
(statements[7] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 254.0, Position.DUMMY)
val errors = ErrorReporterForTests()
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
errors.errors.size shouldBe 6
errors.errors[0] shouldContain "out of range"
errors.errors[1] shouldContain "WORD doesn't match"
errors.errors[2] shouldContain "out of range"
errors.errors[3] shouldContain "BYTE doesn't match"
errors.errors[4] shouldContain "out of range"
errors.errors[5] shouldContain "BYTE doesn't match"
}
})

View File

@ -162,6 +162,14 @@ class TestNumericLiteral: FunSpec({
NumericLiteral.optimalInteger(-1000, Position.DUMMY).number shouldBe -1000.0
NumericLiteral.optimalInteger(1000u, Position.DUMMY).type shouldBe DataType.UWORD
NumericLiteral.optimalInteger(1000u, Position.DUMMY).number shouldBe 1000.0
NumericLiteral.optimalInteger(DataType.UBYTE, DataType.UWORD, 1, Position.DUMMY).type shouldBe DataType.UWORD
NumericLiteral.optimalInteger(DataType.UWORD, DataType.UBYTE, 1, Position.DUMMY).type shouldBe DataType.UWORD
NumericLiteral.optimalInteger(DataType.UWORD, null, 1, Position.DUMMY).type shouldBe DataType.UWORD
NumericLiteral.optimalInteger(DataType.UBYTE, DataType.UBYTE, -1, Position.DUMMY).type shouldBe DataType.BYTE
NumericLiteral.optimalInteger(DataType.UBYTE, null, -1, Position.DUMMY).type shouldBe DataType.BYTE
NumericLiteral.optimalInteger(DataType.UWORD, DataType.UWORD, -1, Position.DUMMY).type shouldBe DataType.WORD
NumericLiteral.optimalInteger(DataType.UWORD, null, -1, Position.DUMMY).type shouldBe DataType.WORD
}
test("optimalNumeric") {
@ -183,6 +191,22 @@ class TestNumericLiteral: FunSpec({
NumericLiteral.optimalNumeric(1234.0, Position.DUMMY).number shouldBe 1234.0
NumericLiteral.optimalNumeric(-1234.0, Position.DUMMY).type shouldBe DataType.WORD
NumericLiteral.optimalNumeric(-1234.0, Position.DUMMY).number shouldBe -1234.0
NumericLiteral.optimalNumeric(DataType.UBYTE, DataType.UWORD, 1.0, Position.DUMMY).type shouldBe DataType.UWORD
NumericLiteral.optimalNumeric(DataType.UWORD, DataType.UBYTE, 1.0, Position.DUMMY).type shouldBe DataType.UWORD
NumericLiteral.optimalNumeric(DataType.UWORD, null, 1.0, Position.DUMMY).type shouldBe DataType.UWORD
NumericLiteral.optimalNumeric(DataType.UBYTE, DataType.UBYTE, -1.0, Position.DUMMY).type shouldBe DataType.BYTE
NumericLiteral.optimalNumeric(DataType.UBYTE, null, -1.0, Position.DUMMY).type shouldBe DataType.BYTE
NumericLiteral.optimalNumeric(DataType.UWORD, DataType.UWORD, -1.0, Position.DUMMY).type shouldBe DataType.WORD
NumericLiteral.optimalNumeric(DataType.UWORD, null, -1.0, Position.DUMMY).type shouldBe DataType.WORD
NumericLiteral.optimalNumeric(DataType.UBYTE, DataType.UWORD, 1.234, Position.DUMMY).type shouldBe DataType.FLOAT
NumericLiteral.optimalNumeric(DataType.UWORD, DataType.UBYTE, 1.234, Position.DUMMY).type shouldBe DataType.FLOAT
NumericLiteral.optimalNumeric(DataType.UWORD, null, 1.234, Position.DUMMY).type shouldBe DataType.FLOAT
NumericLiteral.optimalNumeric(DataType.UBYTE, DataType.UBYTE, -1.234, Position.DUMMY).type shouldBe DataType.FLOAT
NumericLiteral.optimalNumeric(DataType.UBYTE, null, -1.234, Position.DUMMY).type shouldBe DataType.FLOAT
NumericLiteral.optimalNumeric(DataType.UWORD, DataType.UWORD, -1.234, Position.DUMMY).type shouldBe DataType.FLOAT
NumericLiteral.optimalNumeric(DataType.UWORD, null, -1.234, Position.DUMMY).type shouldBe DataType.FLOAT
}
test("cast can change value") {

View File

@ -896,8 +896,7 @@ main {
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(C64Target(), false, src, writeAssembly = false, errors = errors)!!
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
val program = result.compilerAst
val st = program.entrypoint.statements
st.size shouldBe 1
@ -909,4 +908,49 @@ main {
ifexpr.truevalue shouldBe instanceOf<NumericLiteral>()
ifexpr.falsevalue shouldBe instanceOf<NumericLiteral>()
}
test("correct data types of numeric literals in word/byte scenario") {
val src = """
main {
sub start() {
const uword WIDTH = 40
const uword WIDER = 400
cx16.r0 = cx16.r0-1+WIDTH
cx16.r0 = cx16.r0-1+WIDER
cx16.r0 = cx16.r0L * 5 ; byte multiplication
cx16.r0 = cx16.r0L * ${'$'}0005 ; word multiplication
}
}"""
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
val program = result.compilerAst
val st = program.entrypoint.statements
st.size shouldBe 6
val v1 = (st[2] as Assignment).value as BinaryExpression
v1.operator shouldBe "+"
(v1.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0")
(v1.right as NumericLiteral).type shouldBe DataType.UWORD
(v1.right as NumericLiteral).number shouldBe 39
val v2 = (st[3] as Assignment).value as BinaryExpression
v2.operator shouldBe "+"
(v2.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0")
(v2.right as NumericLiteral).type shouldBe DataType.UWORD
(v2.right as NumericLiteral).number shouldBe 399
val v3 = (st[4] as Assignment).value as TypecastExpression
v3.type shouldBe DataType.UWORD
val v3e = v3.expression as BinaryExpression
v3e.operator shouldBe "*"
(v3e.left as IdentifierReference).nameInSource shouldBe listOf("cx16","r0L")
(v3e.right as NumericLiteral).type shouldBe DataType.UBYTE
(v3e.right as NumericLiteral).number shouldBe 5
val v4 = (st[5] as Assignment).value as BinaryExpression
v4.operator shouldBe "*"
val v4t = v4.left as TypecastExpression
v4t.type shouldBe DataType.UWORD
(v4t.expression as IdentifierReference).nameInSource shouldBe listOf("cx16","r0L")
(v4.right as NumericLiteral).type shouldBe DataType.UWORD
(v4.right as NumericLiteral).number shouldBe 5
}
})

View File

@ -464,7 +464,9 @@ private fun IntegerliteralContext.toAst(): NumericLiteralNode {
}
}
2 -> {
if(literalText.length>8)
if(literalText.length>16)
datatype = DataType.LONG
else if(literalText.length>8)
datatype = DataType.UWORD
try {
integer = literalText.toInt(2)
@ -473,7 +475,9 @@ private fun IntegerliteralContext.toAst(): NumericLiteralNode {
}
}
16 -> {
if(literalText.length>2)
if(literalText.length>4)
datatype = DataType.LONG
else if(literalText.length>2)
datatype = DataType.UWORD
try {
integer = literalText.toInt(16)
@ -558,7 +562,7 @@ private fun ExpressionContext.toAst(insideParentheses: Boolean=false) : Expressi
if (rangefrom!=null && rangeto!=null) {
val defaultstep = if(rto.text == "to") 1 else -1
val step = rangestep?.toAst() ?: NumericLiteral(DataType.UBYTE, defaultstep.toDouble(), toPosition())
val step = rangestep?.toAst() ?: NumericLiteral.optimalInteger(defaultstep, toPosition())
return RangeExpression(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
}

View File

@ -119,7 +119,13 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
else -> throw ExpressionError("can only take bitwise inversion of int", constval.position)
}
"not" -> NumericLiteral.fromBoolean(constval.number==0.0, constval.position)
"^" -> NumericLiteral(DataType.UBYTE, (constval.number.toInt() ushr 16 and 255).toDouble(), constval.position) // bank
"^" -> {
val const = constval.number.toInt()
return if(const>0xffffff)
null // number is more than 24 bits; bank byte exceeds 255
else
NumericLiteral(DataType.UBYTE, (const ushr 16 and 255).toDouble(), constval.position) // bank
}
"<<" -> NumericLiteral(DataType.UWORD, (constval.number.toInt() and 65535).toDouble(), constval.position) // address
else -> throw FatalAstException("invalid operator")
}
@ -226,7 +232,7 @@ class BinaryExpression(
dt
} else
dt
} catch (x: FatalAstException) {
} catch (_: FatalAstException) {
InferredTypes.unknown()
}
}
@ -256,6 +262,25 @@ class BinaryExpression(
// word + word -> word
// a combination with a float will be float (but give a warning about this!)
// if left or right is a numeric literal, and its value fits in the type of the other operand, use the other's operand type
// EXCEPTION: if the numeric value is a word and the other operand is a byte type (to allow v * $0008 for example)
if (left is NumericLiteral) {
if(!(leftDt in WordDatatypes && rightDt in ByteDatatypes)) {
val optimal = NumericLiteral.optimalNumeric(rightDt, null, left.number, left.position)
if (optimal.type != leftDt && optimal.type isAssignableTo rightDt) {
return optimal.type to left
}
}
}
if (right is NumericLiteral) {
if(!(rightDt in WordDatatypes && leftDt in ByteDatatypes)) {
val optimal = NumericLiteral.optimalNumeric(leftDt, null, right.number, right.position)
if (optimal.type != rightDt && optimal.type isAssignableTo leftDt) {
return optimal.type to right
}
}
}
return when (leftDt) {
DataType.BOOL -> {
return if(rightDt==DataType.BOOL)
@ -508,6 +533,18 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
}
}
init {
when(type) {
DataType.UBYTE -> require(numbervalue in 0.0..255.0)
DataType.BYTE -> require(numbervalue in -128.0..127.0)
DataType.UWORD -> require(numbervalue in 0.0..65535.0)
DataType.WORD -> require(numbervalue in -32768.0..32767.0)
DataType.LONG -> require(numbervalue in -2147483647.0..2147483647.0)
DataType.BOOL -> require(numbervalue==0.0 || numbervalue==1.0)
else -> {}
}
}
override val isSimple = true
override fun copy() = NumericLiteral(type, number, position)
@ -515,23 +552,11 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
fun fromBoolean(bool: Boolean, position: Position) =
NumericLiteral(DataType.BOOL, if(bool) 1.0 else 0.0, position)
fun optimalNumeric(origType1: DataType, origType2: DataType?, value: Number, position: Position) : NumericLiteral {
val optimal = optimalNumeric(value, position)
val largestOrig = if(origType2==null) origType1 else if(origType1.largerThan(origType2)) origType1 else origType2
return if(largestOrig.largerThan(optimal.type))
NumericLiteral(largestOrig, optimal.number, position)
else
optimal
}
fun optimalNumeric(origType1: DataType, origType2: DataType?, value: Number, position: Position) : NumericLiteral =
fromOptimal(optimalNumeric(value, position), origType1, origType2, position)
fun optimalInteger(origType1: DataType, origType2: DataType?, value: Int, position: Position): NumericLiteral {
val optimal = optimalInteger(value, position)
val largestOrig = if(origType2==null) origType1 else if(origType1.largerThan(origType2)) origType1 else origType2
return if(largestOrig.largerThan(optimal.type))
NumericLiteral(largestOrig, optimal.number, position)
else
optimal
}
fun optimalInteger(origType1: DataType, origType2: DataType?, value: Int, position: Position): NumericLiteral =
fromOptimal(optimalInteger(value, position), origType1, origType2, position)
fun optimalNumeric(value: Number, position: Position): NumericLiteral {
val digits = floor(value.toDouble()) - value.toDouble()
@ -570,6 +595,23 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
else -> throw FatalAstException("unsigned integer overflow: $value")
}
}
private fun fromOptimal(optimal: NumericLiteral, origType1: DataType, origType2: DataType?, position: Position): NumericLiteral {
var largestOrig = if(origType2==null) origType1 else if(origType1.largerThan(origType2)) origType1 else origType2
return if(largestOrig.largerThan(optimal.type)) {
if(optimal.number<0 && largestOrig !in SignedDatatypes) {
when(largestOrig){
DataType.BOOL -> {}
DataType.UBYTE -> largestOrig = DataType.BYTE
DataType.UWORD -> largestOrig = DataType.WORD
else -> throw FatalAstException("invalid dt")
}
}
NumericLiteral(largestOrig, optimal.number, position)
}
else
optimal
}
}
val asBooleanValue: Boolean = number != 0.0
@ -655,13 +697,13 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
}
DataType.BYTE -> {
if(targettype==DataType.UBYTE) {
if(number in -128.0..0.0)
if(number in -128.0..0.0 && !implicit)
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toUByte().toDouble(), position))
else if(number in 0.0..255.0)
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
}
if(targettype==DataType.UWORD) {
if(number in -32768.0..0.0)
if(number in -32768.0..0.0 && !implicit)
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toUShort().toDouble(), position))
else if(number in 0.0..65535.0)
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
@ -689,13 +731,13 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
if(targettype==DataType.BYTE && number >= -128 && number <=127)
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
if(targettype==DataType.UBYTE) {
if(number in -128.0..0.0)
if(number in -128.0..0.0 && !implicit)
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toUByte().toDouble(), position))
else if(number in 0.0..255.0)
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))
}
if(targettype==DataType.UWORD) {
if(number in -32768.0 .. 0.0)
if(number in -32768.0 .. 0.0 && !implicit)
return ValueAfterCast(true, null, NumericLiteral(targettype, number.toInt().toUShort().toDouble(), position))
else if(number in 0.0..65535.0)
return ValueAfterCast(true, null, NumericLiteral(targettype, number, position))

View File

@ -102,7 +102,7 @@ msb (x)
can do that using ``bankof(x)``.
bankof (x)
Get the 'bank' byte from the value x. This means bits 16-24 of that value: bankof($1234567) = $12.
Get the 'bank' byte from the value x. This means bits 16-24 of that value: bankof($123456) = $12.
(To get the 16 bit address out of a value simply use ``x & $ffff``)
If x is a word or smaller, bankof(x) will always be zero.
You can consider this function equivalent to the expression ``lsb(x >> 16)``.

View File

@ -3,7 +3,7 @@ TODO
make a compiler switch to disable footgun warnings
what to do with bankof(): keep it? add another syntax like \`value or ^value to get the bank byte?
-> added bankof() to get the bank byte of a large integer
-> added msw() and lsw() . note: msw() on a 24 bits constant can ALSO be used to get the bank byte because the value, while a word type, will be <=255
-> added unary ^ operator as alternative to bankof()
-> added unary << operator as alternative to lsw() / lsb(x>>16)

View File

@ -190,13 +190,26 @@ which is the PETSCII value for that character. You can prefix it with the desire
**bytes versus words:**
- When an integer value ranges from 0..255 the compiler sees it as a ``ubyte``. For -128..127 it's a ``byte``.
- When an integer value ranges from 256..65535 the compiler sees it as a ``uword``. For -32768..32767 it's a ``word``.
- When a hex number has 3 or 4 digits, for example ``$0004``, it is seen as a ``word`` otherwise as a ``byte``.
- When a binary number has 9 to 16 digits, for example ``%1100110011``, it is seen as a ``word`` otherwise as a ``byte``.
- If the number fits in a byte but you really require it as a word value, you'll have to explicitly cast it: ``60 as uword``
or you can use the full word hexadecimal notation ``$003c``.
Prog8 tries to determine the data type of integer values according to the table below,
and sometimes the context in which they are used.
========================= =================
value datatype
========================= =================
-128 .. 127 byte
0 .. 255 ubyte
-32768 .. 32767 word
0 .. 65535 uword
-2147483647 .. 2147483647 long (only for const)
========================= =================
If the number fits in a byte but you really require it as a word value, you'll have to explicitly cast it: ``60 as uword``
or you can use the full word hexadecimal notation ``$003c``. This is useful in expressions where you want a calcuation
to be done on word values, and don't want to explicitly have to cast everything all the time. For instance::
ubyte column
uword offset = column * 64 ; does (column * 64) as uword, wrong result?
uword offset = column * $0040 ; does (column as uword) * 64 , a word calculation
Only for ``const`` numbers, you can use larger values (32 bits signed integers). The compiler can handle those
internally in expressions. As soon as you have to actually store it into a variable,
@ -226,8 +239,6 @@ This saves a lot of memory and may be faster as well.
Floating point numbers
^^^^^^^^^^^^^^^^^^^^^^
You can use underscores to group digits to make long numbers more readable.
Floats are stored in the 5-byte 'MFLPT' format that is used on CBM machines.
Floating point support is available on the c64 and cx16 (and virtual) compiler targets.
On the c64 and cx16, the rom routines are used for floating point operations,
@ -243,6 +254,10 @@ to worry about this yourself)
The largest 5-byte MFLPT float that can be stored is: **1.7014118345e+38** (negative: **-1.7014118345e+38**)
You can use underscores to group digits in floating point literals to make long numbers more readable:
any underscores in the number are ignored by the compiler.
For instance ``30_000.999_999`` is a valid floating point number 30000.999999.
Arrays
^^^^^^

View File

@ -28,7 +28,7 @@ adpcm {
; belong to the left channel and -if it's stereo- the next 4 bytes belong to the right channel.
ubyte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
byte[] t_index = [ -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8]
uword[] @split t_step = [
7, 8, 9, 10, 11, 12, 13, 14,
16, 17, 19, 21, 23, 25, 28, 31,
@ -92,7 +92,7 @@ adpcm {
; elif predicted < -32767:
; predicted = - 32767
index += t_index[nibble]
index += t_index[nibble] as ubyte
if_neg
index = 0
else if index >= len(t_step)-1
@ -128,7 +128,7 @@ adpcm {
; elif predicted < -32767:
; predicted = - 32767
index_2 += t_index[nibble]
index_2 += t_index[nibble] as ubyte
if_neg
index_2 = 0
else if index_2 >= len(t_step)-1

View File

@ -4,16 +4,28 @@
main {
sub start() {
@($2005) = 0
txt.print_ub(get_indexed_byte($2000, 5))
txt.nl()
@($2005) = 123
txt.print_ub(get_indexed_byte($2000, 5))
ubyte @shared ub = -1
uword @shared uw = -5555
txt.print_ubhex(bankof($123456), true)
txt.spc()
txt.print_ubhex(msw($123456), true)
txt.spc()
txt.print_ubhex(^$123456, true)
txt.nl()
}
txt.print_uwhex(<<$1234567, true)
txt.spc()
txt.print_uwhex(lsw($1234567), true)
txt.spc()
txt.print_uwhex($1234567 & $ffff, true)
txt.nl()
sub get_indexed_byte(uword pointer @R0, ubyte index @R1) -> ubyte {
return @(cx16.r0 + cx16.r1L)
txt.print_uwhex(<<$123456, true)
txt.spc()
txt.print_uwhex(lsw($123456), true)
txt.spc()
txt.print_uwhex($123456 & $ffff, true)
txt.nl()
}
}