mirror of
https://github.com/irmen/prog8.git
synced 2025-12-19 20:17:17 +00:00
'hack' to allow unsigned long constants such as $ffffffff to be assigned to longs without casts
This commit is contained in:
@@ -58,9 +58,9 @@ What does Prog8 provide?
|
||||
|
||||
- all advantages of a higher level language over having to write assembly code manually
|
||||
- programs run very fast because it's compiled to native machine code
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- compiled code is very small; much smaller than equivalent C code compiled with CC65, and usually runs faster as well
|
||||
- modularity, symbol scoping, subroutines. No need for forward declarations.
|
||||
- various data types other than just bytes (16-bit words, floats, strings)
|
||||
- various data types other than just bytes (16-bit words, long integers, floats, strings)
|
||||
- Structs and typed pointers
|
||||
- floating point math is supported on certain targets
|
||||
- access to most Kernal ROM routines as external subroutine definitions you can call normally
|
||||
@@ -82,7 +82,7 @@ What does Prog8 provide?
|
||||
- supports the sixteen 'virtual' 16-bit registers R0 - R15 from the Commander X16 (also available on other targets)
|
||||
- encode strings and characters into petscii or screencodes or even other encodings
|
||||
- Automatic ROM/RAM bank switching on certain compiler targets when calling routines in other banks
|
||||
- 50 Kb of available program RAM size on the C64 by default; because Basic ROM is banked out altogether
|
||||
- 50 Kb of available program RAM size on the C64 by default (41 Kb on the C128) because Basic ROM is banked out by default
|
||||
|
||||
*Rapid edit-compile-run-debug cycle:*
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
expr))
|
||||
}
|
||||
if(rightCv!=null && rightCv.number<0) {
|
||||
val value = if(leftDt.isBytes) 256+rightCv.number else if(leftDt.isWords) 65536+rightCv.number else 0xffffffffL+rightCv.number
|
||||
val value = if(leftDt.isBytes) 256+rightCv.number else if(leftDt.isWords) 65536+rightCv.number else (0x100000000L+rightCv.number).toLong().toInt().toDouble()
|
||||
return listOf(IAstModification.ReplaceNode(
|
||||
expr.right,
|
||||
NumericLiteral(leftDt.getOrUndef().base, value, expr.right.position),
|
||||
|
||||
@@ -17,13 +17,14 @@ import prog8.code.core.BaseDataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
class TestConst: FunSpec({
|
||||
|
||||
val outputDir = tempdir().toPath()
|
||||
|
||||
|
||||
test("const folding multiple scenarios +/-") {
|
||||
val source = """
|
||||
main {
|
||||
@@ -224,7 +225,7 @@ class TestConst: FunSpec({
|
||||
}
|
||||
|
||||
test("const pointer variable indexing works") {
|
||||
val src="""
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const uword pointer=$1000
|
||||
@@ -233,11 +234,11 @@ main {
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
|
||||
compileText(C64Target(), optimize = false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("advanced const folding of known library functions") {
|
||||
val src="""
|
||||
val src = """
|
||||
%import floats
|
||||
%import math
|
||||
%import strings
|
||||
@@ -260,7 +261,7 @@ main {
|
||||
}
|
||||
|
||||
test("const address-of memory mapped arrays") {
|
||||
val src= """
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
&uword[30] @nosplit wb = $2000
|
||||
@@ -277,10 +278,10 @@ main {
|
||||
st.size shouldBe 7
|
||||
((st[0] as VarDecl).value as NumericLiteral).number shouldBe 0x2000
|
||||
((st[1] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00
|
||||
((st[2] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00+2*30
|
||||
((st[2] as VarDecl).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
|
||||
((st[3] as Assignment).value as NumericLiteral).number shouldBe 0x9e00
|
||||
((st[4] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30
|
||||
((st[5] as Assignment).value as NumericLiteral).number shouldBe 0x9e00+2*30
|
||||
((st[4] as Assignment).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
|
||||
((st[5] as Assignment).value as NumericLiteral).number shouldBe 0x9e00 + 2 * 30
|
||||
}
|
||||
|
||||
test("address of a memory mapped variable") {
|
||||
@@ -296,7 +297,7 @@ main {
|
||||
&uword[20] @shared @nosplit wa = HIGH_MEMORY_START
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true)!!
|
||||
val result = compileText(Cx16Target(), optimize = false, src, outputDir, writeAssembly = true)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 7
|
||||
val arrayDeclV = (st[2] as VarDecl).value
|
||||
@@ -306,7 +307,7 @@ main {
|
||||
}
|
||||
|
||||
test("address of a const uword pointer array expression") {
|
||||
val src= """
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const uword buffer = 2000
|
||||
@@ -328,7 +329,7 @@ main {
|
||||
}
|
||||
|
||||
test("out of range const byte and word give correct error") {
|
||||
var src="""
|
||||
var src = """
|
||||
main {
|
||||
sub start() {
|
||||
const byte MIN_BYTE = -129
|
||||
@@ -339,7 +340,7 @@ main {
|
||||
}"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors[0] shouldContain "out of range"
|
||||
errors.errors[1] shouldContain "out of range"
|
||||
@@ -348,7 +349,7 @@ main {
|
||||
}
|
||||
|
||||
test("out of range var byte and word give correct error") {
|
||||
var src="""
|
||||
var src = """
|
||||
main {
|
||||
sub start() {
|
||||
byte @shared v_MIN_BYTE = -129
|
||||
@@ -359,7 +360,7 @@ main {
|
||||
}"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 8
|
||||
errors.errors[0] shouldContain "out of range"
|
||||
errors.errors[2] shouldContain "out of range"
|
||||
@@ -368,7 +369,7 @@ main {
|
||||
}
|
||||
|
||||
test("out of range const byte and word no errors with explicit cast if possible") {
|
||||
var src="""
|
||||
var src = """
|
||||
main {
|
||||
sub start() {
|
||||
const byte MIN_BYTE = -129 as byte ; still error
|
||||
@@ -379,16 +380,16 @@ main {
|
||||
}"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 4
|
||||
errors.errors[0] shouldContain(":4:31: const declaration needs a compile-time constant")
|
||||
errors.errors[1] shouldContain(":4:32: no cast available")
|
||||
errors.errors[2] shouldContain(":5:31: const declaration needs a compile-time constant")
|
||||
errors.errors[3] shouldContain(":5:32: no cast available")
|
||||
errors.errors[0] shouldContain (":4:31: const declaration needs a compile-time constant")
|
||||
errors.errors[1] shouldContain (":4:32: no cast available")
|
||||
errors.errors[2] shouldContain (":5:31: const declaration needs a compile-time constant")
|
||||
errors.errors[3] shouldContain (":5:32: no cast available")
|
||||
}
|
||||
|
||||
test("out of range var byte and word no errors with explicit cast if possible") {
|
||||
var src="""
|
||||
var src = """
|
||||
main {
|
||||
sub start() {
|
||||
byte @shared v_min_byte2 = -129 as byte ; still error
|
||||
@@ -399,14 +400,14 @@ main {
|
||||
}"""
|
||||
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors=errors) shouldBe null
|
||||
compileText(C64Target(), true, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain(":4:37: no cast available")
|
||||
errors.errors[1] shouldContain(":5:37: no cast available")
|
||||
errors.errors[0] shouldContain (":4:37: no cast available")
|
||||
errors.errors[1] shouldContain (":5:37: no cast available")
|
||||
}
|
||||
|
||||
test("const evaluation of signed bitwise operations") {
|
||||
val src="""
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
byte @shared a = -1
|
||||
@@ -455,7 +456,7 @@ main {
|
||||
}
|
||||
|
||||
test("const long with small values") {
|
||||
val src="""
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const long notkaputt = 42
|
||||
@@ -464,4 +465,34 @@ main {
|
||||
}"""
|
||||
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("const long with large unsigned long values should be converted to signed longs") {
|
||||
val src = $$"""
|
||||
main {
|
||||
sub start() {
|
||||
long @shared l1 = $e1fa84c6
|
||||
long @shared l2 = -1
|
||||
long @shared l3 = $ffffffff
|
||||
long @shared l4 = $7fffffff
|
||||
|
||||
l1 ^= -1
|
||||
l2 ^= $ffffffff
|
||||
l3 ^= $7fffffff
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||
val result = compileText(VMTarget(), true, src, outputDir, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 12
|
||||
val a = st.filterIsInstance<Assignment>()
|
||||
(a[0].value as NumericLiteral).number shouldBe -503675706.0
|
||||
(a[1].value as NumericLiteral).number shouldBe -1.0
|
||||
(a[2].value as NumericLiteral).number shouldBe -1.0
|
||||
(a[3].value as NumericLiteral).number shouldBe 0x7fffffffL.toDouble()
|
||||
((a[4].value as BinaryExpression).right as NumericLiteral).number shouldBe -1.0
|
||||
((a[5].value as BinaryExpression).right as NumericLiteral).number shouldBe -1.0
|
||||
((a[6].value as BinaryExpression).right as NumericLiteral).number shouldBe 0x7fffffffL.toDouble()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
|
||||
@@ -385,6 +385,13 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
|
||||
BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2)
|
||||
else -> throw FatalAstException(terminal.text)
|
||||
}
|
||||
|
||||
// TODO "hack" to allow unsigned long constants to be used as values for signed longs, without needing a cast
|
||||
if(integer.second.isLong && integer.first > Integer.MAX_VALUE) {
|
||||
val signedLong = integer.first.toLong().toInt()
|
||||
return NumericLiteral(integer.second, signedLong.toDouble(), ctx.toPosition())
|
||||
}
|
||||
|
||||
return NumericLiteral(integer.second, integer.first, ctx.toPosition())
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@ Language Features
|
||||
|
||||
- it is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
|
||||
- the compiled programs run very fast, because compilation to highly efficient native machine code.
|
||||
- code often is smaller and faster than equivalent C code compiled with CC65 or even LLVM-MOS
|
||||
- compiled code is very compact; it is much smaller and usually also runs faster than equivalent C code compiled with CC65
|
||||
- provides a convenient and fast edit/compile/run cycle by being able to directly launch
|
||||
the compiled program in an emulator and provide debugging information to this emulator.
|
||||
- the language looks like a mix of Python and C so should be quite easy to learn
|
||||
@@ -78,7 +78,7 @@ Language Features
|
||||
still able to directly use memory addresses and ROM subroutines,
|
||||
and inline assembly to have full control when every register, cycle or byte matters
|
||||
- Variables are all allocated statically, no memory allocation overhead
|
||||
- Variable data types include signed and unsigned bytes and words, arrays, strings.
|
||||
- Variable data types include signed and unsigned bytes and words, long integers, floats, arrays, and strings.
|
||||
- Structs and typed pointers
|
||||
- Tight control over Zeropage usage
|
||||
- Programs can be restarted after exiting (i.e. run them multiple times without having to reload everything), due to automatic variable (re)initializations.
|
||||
|
||||
@@ -9,22 +9,31 @@ main {
|
||||
}
|
||||
|
||||
sub start() {
|
||||
long @shared l1 = $e1fa84c6
|
||||
long @shared l2 = -1
|
||||
long @shared l3 = $ffffffff
|
||||
long @shared l4 = $7fffffff
|
||||
|
||||
l1 ^= -1
|
||||
l2 ^= $ffffffff
|
||||
l3 ^= $7fffffff
|
||||
|
||||
|
||||
; cx16.r5L = 10
|
||||
; txt.print_l(cx16.r5L as long * $2000)
|
||||
; txt.spc()
|
||||
; txt.print_l(($2000 as long) * cx16.r5L) ; TODO fix long result? or wait till the long consts have landed?
|
||||
; txt.nl()
|
||||
|
||||
|
||||
^^element myElement = $6000
|
||||
myElement.y = $12345678
|
||||
long @shared lv = $10101010
|
||||
cx16.r0 = $ffff
|
||||
|
||||
myElement.y += lv+cx16.r0
|
||||
txt.print_ulhex(myElement.y, true)
|
||||
txt.spc()
|
||||
myElement.y -= lv+cx16.r0
|
||||
txt.print_ulhex(myElement.y, true)
|
||||
; ^^element myElement = $6000
|
||||
; myElement.y = $12345678
|
||||
; long @shared lv = $10101010
|
||||
; cx16.r0 = $ffff
|
||||
;
|
||||
; myElement.y += lv+cx16.r0
|
||||
; txt.print_ulhex(myElement.y, true)
|
||||
; txt.spc()
|
||||
; myElement.y -= lv+cx16.r0
|
||||
; txt.print_ulhex(myElement.y, true)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user