'hack' to allow unsigned long constants such as $ffffffff to be assigned to longs without casts

This commit is contained in:
Irmen de Jong
2025-10-20 00:33:40 +02:00
parent e5939be0bd
commit ebc738b132
6 changed files with 90 additions and 43 deletions

View File

@@ -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:*

View File

@@ -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),

View File

@@ -17,6 +17,7 @@ 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
@@ -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()
}
})

View File

@@ -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())
}

View File

@@ -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.

View File

@@ -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)
}
}