Compare commits

..
Author SHA1 Message Date
Irmen de Jong 79039b66d4 beta6 2025-11-07 23:40:03 +01:00
Irmen de Jong 833e463525 String indexing bound check now includes the terminating 0 character. Also fix negative indexes on strings.
fixes #190
2025-11-07 23:20:44 +01:00
Irmen de Jong 6611e4e092 improve error message 2025-11-07 01:22:41 +01:00
Irmen de Jong 1f31cb18e4 IR: fix missing source lines in p8ir file 2025-11-06 00:39:31 +01:00
Irmen de Jong bc8ba252a5 made Position line, col, endcol all 1-based
added a precise (but slow) back face culling routine to the cobra-mk3 example
2025-11-05 00:25:43 +01:00
Irmen de Jong c353dd40bf put on..call jumplist in correct scope, fixes #197 2025-11-03 23:46:49 +01:00
Irmen de Jong 928ef6bbaa fix on..call parse problem, also struct name cannot be a keyword 2025-11-03 21:21:07 +01:00
Irmen de Jong 45dde856c6 upgrade to kotlin 2.2.21 2025-11-02 20:08:59 +01:00
Irmen de Jong e1ccef4e89 sort the library routines better, updated result of crc32 benchmark 2025-11-02 16:58:49 +01:00
Irmen de Jong 4d3f0ec223 sizeof(string) is now defined 2025-11-01 21:19:30 +01:00
Irmen de Jong b73c958c4a ir immediate syntax fixes 2025-11-01 16:55:00 +01:00
Irmen de Jong 2eac457e1c clarify string assignment (or rather, the lack thereof) 2025-11-01 00:40:52 +01:00
Irmen de Jong 815ef7e654 add check for not yet implement long expression comparisons 2025-11-01 00:26:36 +01:00
Irmen de Jong 0cd8c4f87e add strings.ncopy and nappend (length limited) 2025-10-31 21:22:15 +01:00
Irmen de Jong b02a8ed954 strings can no longer be assigned by-value. Use strings.copy() instead. Fixes #189 2025-10-31 19:22:40 +01:00
Irmen de Jong b1e07f3fdb better error when trying to use a const pointer (which is not supported yet) 2025-10-30 20:58:21 +01:00
Irmen de Jong fc8727f81e allow pointer to pointer assignment in cast codegen, fixes #191 2025-10-30 19:35:44 +01:00
Irmen de Jong ec728bad52 fix certain float array assignment, fixes #193 2025-10-30 01:45:25 +01:00
Irmen de Jong a5e827e40f implement some more long array operations 2025-10-30 01:20:59 +01:00
Irmen de Jong 34061c5a63 implement inplace bitwise operations on long struct fields 2025-10-28 23:44:34 +01:00
Irmen de Jong 9c2bcab4a5 fix more ptr/long issues 2025-10-28 23:08:12 +01:00
Irmen de Jong 2f07f41664 detect self-referencing aliases 2025-10-23 21:24:14 +02:00
Irmen de Jong 7027304597 put some upper bounds on more internal compiler loops 2025-10-23 20:37:44 +02:00
Irmen de Jong ee75333891 implement more long assignments through pointers 2025-10-21 22:55:46 +02:00
Irmen de Jong 6a70fb0480 deal with invalid sqrt results (negative argument), allow sqrt of longs (like floats) 2025-10-21 21:01:03 +02:00
Irmen de Jong ebc738b132 'hack' to allow unsigned long constants such as $ffffffff to be assigned to longs without casts 2025-10-20 00:33:40 +02:00
Irmen de Jong e5939be0bd some more long operations implemented 2025-10-19 23:03:17 +02:00
Irmen de Jong 0c0affd1bf cx16 diskio.load_size() now returns full long size. Fixes #184 2025-10-19 21:18:46 +02:00
Irmen de Jong c7158fd968 implement more long operations on struct fields 2025-10-17 01:55:11 +02:00
Irmen de Jong de2f3f0d0d tweak TODO messages for longs 2025-10-16 23:20:34 +02:00
Irmen de Jong c0286e3349 added txt.size() to return text screen width and height in 1 call. Also fixed txt.width/height on the C128 (where SCREEN reports 1 less for them...) 2025-10-16 00:33:14 +02:00
85 changed files with 2181 additions and 1055 deletions

No files matched your search

+10 -10
View File
@@ -1,23 +1,23 @@
<component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.20" />
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.21" />
<CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.21/kotlin-stdlib-jdk8-2.2.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.21/kotlin-stdlib-2.2.21.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.21/kotlin-stdlib-jdk7-2.2.21.jar!/" />
</CLASSES>
<JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.21/kotlin-stdlib-jdk8-2.2.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.21/kotlin-stdlib-2.2.21-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.21/kotlin-stdlib-jdk7-2.2.21-javadoc.jar!/" />
</JAVADOC>
<SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.20/kotlin-stdlib-jdk8-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.20/kotlin-stdlib-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.21/kotlin-stdlib-jdk8-2.2.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.21/kotlin-stdlib-2.2.21-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.20/kotlin-stdlib-jdk7-2.2.20-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.21/kotlin-stdlib-jdk7-2.2.21-sources.jar!/" />
</SOURCES>
</library>
</component>
+7 -4
View File
@@ -34,8 +34,11 @@ How to get it/build it
- you can also compile it yourself from source. [Instructions here](https://prog8.readthedocs.io/en/latest/compiling.html).
Note that if you are not using *gradle* to build it, you might have to perform some manual
tasks once to make it compile fully. These are explained in the linked instructions.
- Alternatively, you can also install the compiler as a package on some linux distros:
- Alternatively, you can also install the compiler as a package on some linux distros (which will take care of dependencies automatically):
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
- Finally there's a Homebrew recipe for Mac OS (but also for Linux, and WSL2 on Windows, this also takes care of dependencies automatically):
``brew install prog8``
Community
---------
@@ -58,9 +61,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 +85,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:*
Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

+5 -5
View File
@@ -472,7 +472,7 @@ elite_galaxy {
generate_next_planet()
textelite.num_commands++
}
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
init_market_for_planet()
}
@@ -494,7 +494,7 @@ elite_galaxy {
ubyte pi
for pi in 0 to 255 {
generate_next_planet()
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
if elite_util.prefix_matches(nameptr, elite_planet.name) {
ubyte distance = elite_planet.distance(x, y)
if distance < current_distance {
@@ -532,7 +532,7 @@ elite_galaxy {
; else
; txt.chrout('-')
; txt.spc()
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
elite_planet.display(true, distance)
}
pn++
@@ -548,7 +548,7 @@ elite_galaxy {
str current_name = " " ; 8 max
ubyte pn = 0
current_name = elite_planet.name
void strings.copy(elite_planet.name, current_name)
init(number)
; txt.clear_screen()
; txt.print("Galaxy #")
@@ -569,7 +569,7 @@ elite_galaxy {
generate_next_planet()
ubyte distance = elite_planet.distance(px, py)
if distance <= max_distance {
elite_planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), elite_planet.name)
elite_planet.name[0] = strings.upperchar(elite_planet.name[0])
uword tx = elite_planet.x
uword ty = elite_planet.y
+1 -1
View File
@@ -5,7 +5,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
plugins {
kotlin("jvm") version "2.2.20"
kotlin("jvm") version "2.2.21"
}
allprojects {
@@ -112,6 +112,7 @@ val BuiltinFunctions: Map<String, FSignature> = mapOf(
"sqrt" to FSignature(true, null, FParam("value", *NumericDatatypes)),
"sqrt__ubyte" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UBYTE)),
"sqrt__uword" to FSignature(true, BaseDataType.UBYTE, FParam("value", BaseDataType.UWORD)),
"sqrt__long" to FSignature(true, BaseDataType.UWORD, FParam("value", BaseDataType.LONG)),
"sqrt__float" to FSignature(true, BaseDataType.FLOAT, FParam("value", BaseDataType.FLOAT)),
"divmod" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("divisor", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("quotient", BaseDataType.UBYTE, BaseDataType.UWORD), FParam("remainder", BaseDataType.UBYTE, BaseDataType.UWORD)),
"divmod__ubyte" to FSignature(false, null, FParam("dividend", BaseDataType.UBYTE), FParam("divisor", BaseDataType.UBYTE), FParam("quotient", BaseDataType.UBYTE), FParam("remainder", BaseDataType.UBYTE)),
+9 -1
View File
@@ -6,7 +6,15 @@ import java.nio.file.InvalidPathException
import kotlin.io.path.Path
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
override fun toString(): String = "[$file: line $line col ${startCol}-${endCol}]"
init {
if(!file.startsWith('~') ||!file.endsWith('~'))
require(line>0 && startCol>=0 && endCol>=startCol) {
"Invalid position: $this"
}
}
fun toClickableStr(): String {
if(this===DUMMY)
return ""
@@ -451,7 +451,7 @@ class AsmGen6502Internal (
if(line.length>=4 && invalid.matchesAt(line, 0)) {
errors.err(
"invalid assembly instruction used (not compatible with the 65816 CPU): ${line.trim()}",
Position("<output-assemblycode>", index, 1, 1)
Position("~output-assemblycode~", index+1, 1, 1)
)
}
}
@@ -758,6 +758,15 @@ class AsmGen6502Internal (
CpuRegister.Y -> out(" tay")
}
}
expr.type.isLong -> {
assignExpressionToRegister(expr.index, RegisterOrPair.A)
out(" asl a | asl a")
when (register) {
CpuRegister.A -> {}
CpuRegister.X -> out(" tax")
CpuRegister.Y -> out(" tay")
}
}
expr.type.isFloat -> {
if(options.compTarget.FLOAT_MEM_SIZE != 5)
TODO("support float size other than 5 ${expr.position}")
@@ -844,7 +853,7 @@ class AsmGen6502Internal (
return
}
TargetStorageKind.POINTER -> {
TODO("assign to pointer ${target.position}")
pointerGen.assignByte(PtrTarget(target), 0)
return
}
else -> { }
@@ -854,7 +863,10 @@ class AsmGen6502Internal (
assignExpressionToRegister(value, RegisterOrPair.A)
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A, target.datatype.isSigned, false)
}
target.datatype.isPointer -> TODO("assign expression to pointer ${target.position}")
target.datatype.isPointer -> {
assignExpressionToRegister(value, RegisterOrPair.AX)
pointerGen.assignWordReg(PtrTarget(target), RegisterOrPair.AX)
}
target.datatype.isWord || target.datatype.isPassByRef -> {
assignExpressionToRegister(value, RegisterOrPair.AY)
translateNormalAssignment(
@@ -866,7 +878,7 @@ class AsmGen6502Internal (
}
target.datatype.isLong -> {
if(value is PtNumber) {
val hex = value.asConstInteger()!!.toString(16).padStart(8, '0')
val hex = value.asConstInteger()!!.toLongHex()
when(target.kind) {
TargetStorageKind.VARIABLE -> {
out("""
@@ -882,7 +894,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> pointerGen.assignLong(target.pointer!!, value.number.toInt())
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtIdentifier && value.type.isLong) {
@@ -905,7 +917,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> pointerGen.assignLongVar(target.pointer!!, asmSymbolName(value))
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value is PtTypeCast && value.type.isLong) {
@@ -920,7 +932,7 @@ class AsmGen6502Internal (
TargetStorageKind.ARRAY -> TODO("assign typecasted long to array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> assignExpressionToRegister(value, target.register!!, true)
TargetStorageKind.POINTER -> throw AssemblyError("assign long into pointer ${target.position}")
TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else if(value.value.type.isWord) {
@@ -945,9 +957,7 @@ class AsmGen6502Internal (
sta cx16.$startreg+1""")
signExtendLongVariable("cx16.$startreg", value.value.type.base)
}
TargetStorageKind.POINTER -> {
throw AssemblyError("assign typecasted long into pointer ${target.position}")
}
TargetStorageKind.POINTER -> TODO("assign typecasted long into pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
} else throw AssemblyError("weird casted type")
@@ -956,6 +966,8 @@ class AsmGen6502Internal (
}
} else if(target.kind == TargetStorageKind.REGISTER) {
assignExpressionToRegister(value, target.register!!, true)
} else if(target.kind == TargetStorageKind.VARIABLE) {
assignExpressionToVariable(value, target.asmVarname, target.datatype)
} else {
TODO("assign long expression $value to a target ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
}
@@ -1426,7 +1438,10 @@ $repeatLabel""")
sta $asmvar+3""")
}
BaseDataType.UWORD -> {
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
if(isTargetCpu(CpuType.CPU65C02))
out(" stz $asmvar+2 | stz $asmvar+3")
else
out(" lda #0 | sta $asmvar+2 | sta $asmvar+3")
}
BaseDataType.WORD -> {
out("""
@@ -2109,6 +2124,22 @@ $repeatLabel""")
}
}
internal fun loadIndirectLongIntoR14R15(zpPtrVar: String, offset: UByte) {
out("""
ldy #$offset
lda ($zpPtrVar),y
sta cx16.r14
iny
lda ($zpPtrVar),y
sta cx16.r14+1
iny
lda ($zpPtrVar),y
sta cx16.r14+2
iny
lda ($zpPtrVar),y
sta cx16.r14+3""")
}
internal fun storeIndirectByte(byte: Int, zpPtrVar: String, offset: UByte) {
if (offset > 0u) {
out(" lda #$byte | ldy #$offset | sta ($zpPtrVar),y")
@@ -2245,7 +2276,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #$offset
@@ -2312,7 +2343,7 @@ $repeatLabel""")
}
in Cx16VirtualRegisters -> {
val regname = regs.asScopedNameVirtualReg(DataType.UWORD)
val regname = regs.asScopedNameVirtualReg(DataType.UWORD).joinToString(".")
out("""
lda $regname
ldy #0
@@ -2497,4 +2528,8 @@ internal class SubroutineExtraAsmInfo {
var usedFloatEvalResultVar2 = false
val extraVars = mutableListOf<Triple<BaseDataType, String, UInt?>>()
}
}
internal fun Double.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')
internal fun Int.toLongHex(): String = this.toUInt().toString(16).padStart(8, '0')
@@ -40,7 +40,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
"max__byte", "max__ubyte", "max__word", "max__uword", "max__long" -> funcMax(fcall, resultRegister)
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(fcall, resultRegister, sscope)
"sgn" -> funcSgn(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(fcall, resultRegister, sscope)
"divmod__ubyte" -> funcDivmod(fcall)
"divmod__uword" -> funcDivmodW(fcall)
"rol" -> funcRol(fcall)
@@ -416,7 +416,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
if(arg2.type.isLong) {
if(arg1 is PtIdentifier && arg2 is PtNumber) {
val var1 = asmgen.asmVariableName(arg1)
val hex = arg2.number.toUInt().toString(16).padStart(8, '0')
val hex = arg2.number.toLongHex()
asmgen.out("""
sec
lda $var1
@@ -538,7 +538,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | lsr a | bcc + | ora #$80 |+ | sta $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -557,7 +557,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #$80 | sta $variable+1 |+ ")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -577,7 +577,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
sta $variable+3
+""")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -620,7 +620,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -641,7 +641,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+1 | ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -652,7 +652,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" ror $variable+3 | ror $variable+2 | ror $variable+1 | ror $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -684,7 +684,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" lda $variable | cmp #$80 | rol a | sta $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -703,7 +703,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -721,7 +721,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
inc $variable
+""")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -763,7 +763,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.UWORD -> {
@@ -784,7 +784,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
BaseDataType.LONG -> {
@@ -795,7 +795,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
val variable = asmgen.asmVariableName(what)
asmgen.out(" rol $variable | rol $variable+1 | rol $variable+2 | rol $variable+3")
}
else -> throw AssemblyError("weird type")
else -> throw AssemblyError("weird node")
}
}
else -> throw AssemblyError("weird type")
@@ -1406,7 +1406,7 @@ _jump jmp (${target.asmLabel})
if(left is PtIdentifier) {
val leftvar = asmgen.asmVariableName(left)
if(constRight!=null) {
val hex = constRight.toUInt().toString(16).padStart(8, '0')
val hex = constRight.toLongHex()
asmgen.out("""
lda $leftvar
cmp #$${hex.substring(6,8)}
@@ -1437,7 +1437,7 @@ _jump jmp (${target.asmLabel})
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(left, RegisterOrPair.R14R15_32, left.type.isSigned)
if(constRight!=null) {
val hex = constRight.toUInt().toString(16).padStart(8, '0')
val hex = constRight.toLongHex()
asmgen.out("""
lda cx16.r14
cmp #$${hex.substring(6,8)}
@@ -1492,6 +1492,11 @@ _jump jmp (${target.asmLabel})
stmt: PtIfElse
) {
// this comparison is not part of an expression but part of an if statement, there's no need to save the previous values of the temp registers
if(left !is PtNumber && left !is PtIdentifier || right !is PtNumber && right !is PtIdentifier) {
TODO("long comparison $operator with expressions - use temporary long variable instead to simplify for now ${left.position}")
}
if(operator=="<" || operator ==">=") {
assignmentAsmGen.assignExpressionToRegister(right, RegisterOrPair.R14R15_32, left.type.isSigned)
assignmentAsmGen.assignExpressionToRegister(left, RegisterOrPair.R12R13_32, left.type.isSigned)
@@ -2113,4 +2118,5 @@ _jump jmp (${target.asmLabel})
else -> throw AssemblyError("expected comparison operator")
}
}
}
}
@@ -368,7 +368,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
private fun translateLongExprEqualsNumber(expr: PtExpression, number: Int, falseLabel: String) {
// if L==number
// TODO reuse code from ifElse?
val hex = number.toUInt().toString(16).padStart(8, '0')
val hex = number.toLongHex()
if(expr is PtIdentifier) {
val varname = asmgen.asmVariableName(expr)
asmgen.out("""
@@ -386,7 +386,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
cmp #$${hex.substring(6, 8)}
@@ -424,7 +424,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
// TODO cannot easily preserve R14:R15 on stack because we need the status flags of the comparison in between...
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
cmp $varname2
@@ -493,7 +493,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
bne $falseLabel""")
} else {
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
ora cx16.r14+1
@@ -518,7 +518,7 @@ internal class IfExpressionAsmGen(private val asmgen: AsmGen6502Internal, privat
beq $falseLabel""")
} else {
asmgen.pushLongRegisters(RegisterOrPair.R14R15_32, 1)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32)
asmgen.assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, expr.type.isSigned)
asmgen.out("""
lda cx16.r14
ora cx16.r14+1
@@ -938,7 +938,7 @@ internal class ProgramAndVarsGen(
asmgen.out(" ${it.name} = ${it.address.toHex()}")
}
consts.sortedBy { it.name }.forEach {
if(it.dt==BaseDataType.FLOAT)
if(it.dt.isFloat)
asmgen.out(" ${it.name} = ${it.value}")
else
asmgen.out(" ${it.name} = ${it.value.toHex()}")
@@ -1044,7 +1044,7 @@ internal class ProgramAndVarsGen(
}
dt.isLongArray -> array.map {
val number = it.number!!.toInt()
val hexnum = number.absoluteValue.toString(16).padStart(8, '0')
val hexnum = number.absoluteValue.toLongHex()
if(number>=0)
"$$hexnum"
else
@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
import kotlin.math.log2
@@ -201,7 +202,7 @@ internal class AssignmentAsmGen(
assignRegisterByte(tgt, CpuRegister.A, false, false)
}
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
internal fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
when(assign.source.kind) {
SourceStorageKind.LITERALBOOLEAN -> {
// simple case: assign a constant boolean (0 or 1)
@@ -294,6 +295,19 @@ internal class AssignmentAsmGen(
asmgen.out(" lda $arrayVarName+$indexValue | ldy $arrayVarName+$indexValue+1")
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
}
elementDt.isLong -> {
// it's not an expression so no need to preserve R14-R15
asmgen.out("""
lda $arrayVarName+$indexValue
sta cx16.r14
lda $arrayVarName+$indexValue+1
sta cx16.r14+1
lda $arrayVarName+$indexValue+2
sta cx16.r14+2
lda $arrayVarName+$indexValue+3
sta cx16.r14+3""")
assignRegisterLong(assign.target, RegisterOrPair.R14R15_32)
}
elementDt.isFloat -> {
asmgen.out(" lda #<($arrayVarName+$indexValue) | ldy #>($arrayVarName+$indexValue)")
assignFloatFromAY(assign.target)
@@ -313,6 +327,20 @@ internal class AssignmentAsmGen(
asmgen.out(" lda $arrayVarName,y | ldx $arrayVarName+1,y")
assignRegisterpairWord(assign.target, RegisterOrPair.AX)
}
elementDt.isLong -> {
// it's not an expression so no need to preserve R14-R15
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.Y)
asmgen.out("""
lda $arrayVarName,y
sta cx16.r14
lda $arrayVarName+1,y
sta cx16.r14+1
lda $arrayVarName+2,y
sta cx16.r14+2
lda $arrayVarName+3,y
sta cx16.r14+3""")
assignRegisterLong(assign.target, RegisterOrPair.R14R15_32)
}
elementDt.isFloat -> {
asmgen.loadScaledArrayIndexIntoRegister(value, CpuRegister.A)
asmgen.out("""
@@ -638,21 +666,8 @@ internal class AssignmentAsmGen(
returnDt?.isByteOrBool==true -> assignRegisterByte(target, CpuRegister.A, returnDt.isSigned, false) // function's byte result is in A
returnDt?.isWord==true -> assignRegisterpairWord(target, RegisterOrPair.AY) // function's word result is in AY
returnDt==BaseDataType.STR -> {
val targetDt = target.datatype
when {
targetDt.isString -> {
asmgen.out("""
tax
lda #<${target.asmVarname}
sta P8ZP_SCRATCH_W1
lda #>${target.asmVarname}
sta P8ZP_SCRATCH_W1+1
txa
jsr prog8_lib.strcpy""")
}
targetDt.isUnsignedWord -> assignRegisterpairWord(target, RegisterOrPair.AY)
else -> throw AssemblyError("str return value type mismatch with target")
}
if (target.datatype.isUnsignedWord) assignRegisterpairWord(target, RegisterOrPair.AY)
else throw AssemblyError("str return value type mismatch with target")
}
returnDt== BaseDataType.LONG -> {
// longs are in R14:R15 (r14=lsw, r15=msw)
@@ -787,8 +802,12 @@ internal class AssignmentAsmGen(
return if(translatedOk)
true
else
else {
if(expr.type.isLong && expr.operator in "*/%")
TODO("long multiplication or division ${expr.position}")
anyExprGen.assignAnyExpressionUsingStack(expr, assign)
}
}
private fun optimizedComparison(expr: PtBinaryExpression, assign: AsmAssignment): Boolean {
@@ -1741,49 +1760,9 @@ internal class AssignmentAsmGen(
val left = expr.left
val right = expr.right
when(right) {
is PtIdentifier -> {
if(target.kind == TargetStorageKind.VARIABLE) {
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out("""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}""")
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out(
"""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<cx16.$startreg
ldy #>cx16.$startreg"""
)
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else {
TODO("add/subtract long into ${target.kind} at ${target.position} - use simple expressions and temporary variables for now")
}
}
is PtNumber -> {
asmgen.assignExpressionTo(left, target)
val hex = right.number.toInt().toString(16).padStart(8, '0')
val hex = right.number.toLongHex()
if (target.kind == TargetStorageKind.VARIABLE) {
if (expr.operator == "+") {
asmgen.out("""
@@ -1851,10 +1830,90 @@ internal class AssignmentAsmGen(
sta cx16.$startreg+3""")
}
return true
} else if(target.kind==TargetStorageKind.POINTER) {
// not an expression, no need to save R14/R15
assignExpressionToRegister(expr, RegisterOrPair.R14R15_32, target.datatype.isSigned)
pointergen.assignLongVar(target.pointer!!, "cx16.r14")
return true
} else {
TODO("add/subtract long const into ${target.kind} ${target.position} - use simple expressions and temporary variables for now")
}
}
is PtIdentifier -> {
if(target.kind == TargetStorageKind.VARIABLE) {
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out("""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<${target.asmVarname}
ldy #>${target.asmVarname}""")
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else if(target.kind == TargetStorageKind.REGISTER) {
val startreg = target.register!!.startregname()
asmgen.assignExpressionTo(left, target)
val rightsym = asmgen.asmVariableName(right)
asmgen.out(
"""
lda #<$rightsym
ldy #>$rightsym
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<cx16.$startreg
ldy #>cx16.$startreg"""
)
if (expr.operator == "+") {
asmgen.out(" jsr prog8_lib.long_add_inplace")
} else {
asmgen.out(" jsr prog8_lib.long_sub_inplace")
}
return true
} else {
// isn't an expression, so no need to preserve R12-R15
asmgen.assignExpressionToRegister(expr.left, RegisterOrPair.R12R13_32, left.type.isSigned)
asmgen.assignExpressionToRegister(expr.right, RegisterOrPair.R14R15_32, left.type.isSigned)
if(expr.operator=="+") {
asmgen.out("""
clc
lda cx16.r12
adc cx16.r14
sta cx16.r12
lda cx16.r12+1
adc cx16.r14+1
sta cx16.r12+1
lda cx16.r12+2
adc cx16.r14+2
sta cx16.r12+2
lda cx16.r12+3
adc cx16.r14+3
sta cx16.r12+3""")
} else {
asmgen.out("""
sec
lda cx16.r12
sbc cx16.r14
sta cx16.r12
lda cx16.r12+1
sbc cx16.r14+1
sta cx16.r12+1
lda cx16.r12+2
sbc cx16.r14+2
sta cx16.r12+2
lda cx16.r12+3
sbc cx16.r14+3
sta cx16.r12+3""")
}
asmgen.assignRegister(RegisterOrPair.R12R13_32, target)
return true
}
}
else -> {
val targetreg = target.register
if(targetreg==RegisterOrPair.R14R15_32)
@@ -2617,7 +2676,7 @@ $endLabel""")
}
else -> {}
}
} else if(valueDt.isUnsignedWord) {
} else if(valueDt.isWord) {
when(target.register) {
RegisterOrPair.A,
RegisterOrPair.X,
@@ -2636,7 +2695,38 @@ $endLabel""")
// 'cast' uword into a 16 bits register, just assign it
return assignExpressionToRegister(value, target.register!!, targetDt.isSigned)
}
in combinedLongRegisters -> TODO("assign wprd to long reg ${value.position}")
RegisterOrPair.R0R1_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R0, false)
asmgen.signExtendLongVariable("cx16.r0", valueDt.base)
}
RegisterOrPair.R2R3_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R2, false)
asmgen.signExtendLongVariable("cx16.r2", valueDt.base)
}
RegisterOrPair.R4R5_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R4, false)
asmgen.signExtendLongVariable("cx16.r4", valueDt.base)
}
RegisterOrPair.R6R7_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R6, false)
asmgen.signExtendLongVariable("cx16.r6", valueDt.base)
}
RegisterOrPair.R8R9_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R8, false)
asmgen.signExtendLongVariable("cx16.r8", valueDt.base)
}
RegisterOrPair.R10R11_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R10, false)
asmgen.signExtendLongVariable("cx16.r10", valueDt.base)
}
RegisterOrPair.R12R13_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R12, false)
asmgen.signExtendLongVariable("cx16.r12", valueDt.base)
}
RegisterOrPair.R14R15_32 -> {
assignExpressionToRegister(value, RegisterOrPair.R14, false)
asmgen.signExtendLongVariable("cx16.r14", valueDt.base)
}
else -> {}
}
}
@@ -2671,8 +2761,11 @@ $endLabel""")
} else if(valueDt.isLong && targetDt.isWord) {
// long to word, just take the lsw
assignCastViaLswFunc(value, target)
} else if(valueDt.isPointer && targetDt.isPointer) {
// pointer type A to pointer type B, just assign
assignExpressionToRegister(value, target.register!!, valueDt.isSigned)
} else
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker")
throw AssemblyError("can't cast $valueDt to $targetDt, this should have been checked in the astchecker ${value.position}")
}
}
}
@@ -2812,7 +2905,8 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName")
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_FAC1_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_FAC1_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
else -> throw AssemblyError("weird type")
BaseDataType.LONG -> TODO("cast float to long")
else -> throw AssemblyError("weird type $targetDt")
}
}
@@ -2842,6 +2936,7 @@ $endLabel""")
else
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2878,6 +2973,7 @@ $endLabel""")
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
asmgen.signExtendVariableLsb(targetAsmVarName, BaseDataType.BYTE)
@@ -2911,6 +3007,7 @@ $endLabel""")
BaseDataType.WORD -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2950,6 +3047,7 @@ $endLabel""")
BaseDataType.UWORD, BaseDataType.POINTER -> {
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
lda $sourceAsmVarName
@@ -2979,6 +3077,7 @@ $endLabel""")
BaseDataType.BYTE -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName")
BaseDataType.UWORD -> asmgen.out(" jsr floats.cast_as_uw_into_ya | sty $targetAsmVarName | sta $targetAsmVarName+1")
BaseDataType.WORD -> asmgen.out(" jsr floats.cast_as_w_into_ay | sta $targetAsmVarName | sty $targetAsmVarName+1")
BaseDataType.LONG -> TODO("cast float to long")
else -> throw AssemblyError("weird type")
}
}
@@ -3026,6 +3125,7 @@ $endLabel""")
else
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out("""
st${regs.toString().lowercase()} $targetAsmVarName
@@ -3073,6 +3173,7 @@ $endLabel""")
asmgen.signExtendAYlsb(sourceDt)
asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
asmgen.signExtendLongVariable(targetAsmVarName, sourceDt)
@@ -3108,6 +3209,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs")
}
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3146,6 +3248,7 @@ $endLabel""")
else -> throw AssemblyError("non-word regs")
}
}
BaseDataType.POINTER -> TODO("cast to pointer")
BaseDataType.LONG -> {
when(regs) {
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
@@ -3351,31 +3454,16 @@ $endLabel""")
}
private fun assignVariableString(target: AsmAssignTarget, varName: String) {
when(target.kind) {
TargetStorageKind.VARIABLE -> {
when {
target.datatype.isUnsignedWord -> {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
}
target.datatype.isString || target.datatype.isUnsignedByteArray || target.datatype.isByteArray -> {
asmgen.out("""
lda #<${target.asmVarname}
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda #<$varName
ldy #>$varName
jsr prog8_lib.strcpy""")
}
else -> throw AssemblyError("assign string to incompatible variable type")
}
}
else -> throw AssemblyError("string-assign to weird target")
if (target.kind == TargetStorageKind.VARIABLE) {
if (target.datatype.isUnsignedWord) {
asmgen.out("""
lda #<$varName
ldy #>$varName
sta ${target.asmVarname}
sty ${target.asmVarname}+1""")
} else throw AssemblyError("assign string to incompatible variable type ${target.position}")
}
else throw AssemblyError("string-assign to weird target ${target.position}")
}
private fun assignVariableLong(target: AsmAssignTarget, varName: String, sourceDt: DataType) {
@@ -3479,7 +3567,7 @@ $endLabel""")
else -> throw AssemblyError("wrong dt ${target.position}")
}
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLongVar(target.pointer!!, varName)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -3662,11 +3750,11 @@ $endLabel""")
pointergen.assignFloatAY(IndexedPtrTarget(target))
return
}
asmgen.out(" pha")
asmgen.saveRegisterStack(CpuRegister.A, false)
asmgen.saveRegisterStack(CpuRegister.Y, false)
asmgen.assignExpressionToRegister(target.array.index, RegisterOrPair.A)
asmgen.assignExpressionToVariable(target.array.index, "P8ZP_SCRATCH_REG", DataType.UBYTE)
asmgen.restoreRegisterStack(CpuRegister.Y, false)
asmgen.out(" pla")
asmgen.restoreRegisterStack(CpuRegister.A, false)
asmgen.out("""
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
@@ -3674,6 +3762,7 @@ $endLabel""")
ldy #>${target.asmVarname}
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
lda P8ZP_SCRATCH_REG
jsr floats.set_array_float""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("can't assign float to mem byte")
@@ -3984,7 +4073,18 @@ $endLabel""")
else throw AssemblyError("only combined vreg allowed as long target ${target.position}")
}
TargetStorageKind.ARRAY -> {
TODO("assign 32 bits int into array ${target.position}")
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, CpuRegister.Y)
val arrayVarName = asmgen.asmSymbolName(target.array.variable!!)
val startreg = pairedRegisters.startregname()
asmgen.out("""
lda cx16.$startreg
sta $arrayVarName,y
lda cx16.$startreg+1
sta $arrayVarName+1,y
lda cx16.$startreg+2
sta $arrayVarName+2,y
lda cx16.$startreg+3
sta $arrayVarName+3,y""")
}
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> {
@@ -4004,7 +4104,10 @@ $endLabel""")
sta cx16.$targetStartReg+3""")
}
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> {
val startreg = pairedRegisters.startregname()
pointergen.assignLongVar(target.pointer!!, "cx16.$startreg")
}
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -4499,7 +4602,7 @@ $endLabel""")
stz $startreg+2
stz $startreg+3""")
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLong(target.pointer!!, 0)
TargetStorageKind.VOID -> { /* do nothing */ }
}
return
@@ -4514,7 +4617,7 @@ $endLabel""")
asmgen.out(" lda #$$hexbyte | sta ${target.asmVarname}+$offset")
}
}
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
store(hex.substring(6,8), 0)
store(hex.substring(4,6), 1)
store(hex.substring(2,4), 2)
@@ -4527,7 +4630,7 @@ $endLabel""")
return
}
asmgen.loadScaledArrayIndexIntoRegister(target.array, CpuRegister.Y)
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
asmgen.out("""
lda #$${hex.substring(6,8)}
sta ${target.asmVarname},y
@@ -4542,7 +4645,7 @@ $endLabel""")
TargetStorageKind.REGISTER -> {
require(target.register in combinedLongRegisters)
val regstart = target.register!!.startregname()
val hex = long.toUInt().toString(16).padStart(8, '0')
val hex = long.toLongHex()
asmgen.out("""
lda #$${hex.substring(6,8)}
sta cx16.$regstart
@@ -4553,7 +4656,7 @@ $endLabel""")
lda #$${hex.take(2)}
sta cx16.$regstart+3""")
}
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> pointergen.assignLong(target.pointer!!, long)
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -5309,7 +5412,7 @@ $endLabel""")
TargetStorageKind.ARRAY -> TODO(" - long array ${target.position}")
TargetStorageKind.MEMORY -> throw AssemblyError("memory is bytes not long ${target.position}")
TargetStorageKind.REGISTER -> TODO("32 bits register negate ${target.position}")
TargetStorageKind.POINTER -> throw AssemblyError("can't assign long to pointer, pointers are 16 bits ${target.position}")
TargetStorageKind.POINTER -> TODO("long negate via pointer ${target.position}")
TargetStorageKind.VOID -> { /* do nothing */ }
}
}
@@ -4,6 +4,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
@@ -859,7 +860,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
inc $variable+3
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
clc
lda $variable
@@ -921,7 +922,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
dec $variable+3
+""")
} else {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
sec
lda $variable
@@ -943,7 +944,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
"<<" -> if (value > 0) inplaceLongShiftLeft()
">>" -> if (value > 0) inplaceLongShiftRight()
"|" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
ora #$${hex.substring(6,8)}
@@ -959,7 +960,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"&" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
and #$${hex.substring(6,8)}
@@ -975,7 +976,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
sta $variable+3""")
}
"^" -> {
val hex = value.toUInt().toString(16).padStart(8, '0')
val hex = value.toLongHex()
asmgen.out("""
lda $variable
eor #$${hex.substring(6,8)}
@@ -5,6 +5,7 @@ import prog8.code.ast.*
import prog8.code.core.*
import prog8.codegen.cpu6502.AsmGen6502Internal
import prog8.codegen.cpu6502.VariableAllocator
import prog8.codegen.cpu6502.toLongHex
import kotlin.math.log2
@@ -248,8 +249,10 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
asmgen.loadIndirectFloat(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.FAC1, target)
}
else if(value.type.isLong)
TODO("load long ${value.position}")
else if(value.type.isLong) {
asmgen.loadIndirectLongIntoR14R15(zpPtrVar, offset)
asmgen.assignRegister(RegisterOrPair.R14R15_32, target)
}
else
throw AssemblyError("weird dt ${value.type} in pointer deref assignment ${target.position}")
}
@@ -298,29 +301,34 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
"<<" -> {
if(target.dt.isByte) inplaceByteShiftLeft(target, value)
else if(target.dt.isWord) inplaceWordShiftLeft(target, value)
else if(target.dt.isLong) TODO("inplace long << ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
">>" -> {
if(target.dt.isByte) inplaceByteShiftRight(target, value)
else if(target.dt.isWord) inplaceWordShiftRight(target, value)
else if(target.dt.isLong) TODO("inplace long >> ${target.position}")
else throw AssemblyError("weird dt ${target.position}")
}
"&", "and" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteAnd(target, value)
else if(target.dt.isWord) inplaceWordAnd(target, value)
else if(target.dt.isLong) inplaceLongAnd(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"|", "or" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteOr(target, value)
else if(target.dt.isWord) inplaceWordOr(target, value)
else if(target.dt.isLong) inplaceLongOr(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
"^", "xor" -> {
// byte targets are handled as direct memory access, not a pointer operation anymore however boolean targets are still to be handled here
if(target.dt.isByteOrBool) inplaceByteXor(target, value)
else if(target.dt.isWord) inplaceWordXor(target, value)
else if(target.dt.isLong) inplaceLongXor(target, value)
else throw AssemblyError("weird dt ${target.dt} ${target.position}")
}
else -> throw AssemblyError("invalid operator for in-place modification $operator")
@@ -454,6 +462,42 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
TODO("array ptr assign long var ${target.position}")
}
internal fun assignLongVar(pointer: PtPointerDeref, varName: String) {
val (ptrVar, offset) = deref(pointer)
asmgen.out("""
ldy #$offset
lda $varName
sta ($ptrVar),y
iny
lda $varName+1
sta ($ptrVar),y
iny
lda $varName+2
sta ($ptrVar),y
iny
lda $varName+3
sta ($ptrVar),y""")
}
internal fun assignLong(pointer: PtPointerDeref, long: Int) {
val (ptrVar, offset) = deref(pointer)
val hex = long.toLongHex()
asmgen.out("""
ldy #$offset
lda #$${hex.substring(6,8)}
sta ($ptrVar),y
iny
lda #$${hex.substring(4, 6)}
sta ($ptrVar),y
iny
lda #$${hex.substring(2, 4)}
sta ($ptrVar),y
iny
lda #$${hex.take(2)}
sta ($ptrVar),y""")
}
internal fun operatorDereference(binExpr: PtBinaryExpression): Triple<String, UByte, DataType> {
// the only case we support here is: a.b.c[i] . value
// returns the ZP var to use as a pointer, and a Y register offset (which can be zero), and finally the datatype of the field
@@ -948,19 +992,141 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
private fun inplaceLongAdd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long add with ${value.number} ${target.position}")
SourceStorageKind.VARIABLE -> TODO("inplace long add with ${value.asmVarname} ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("inplace long add with ${value.expression} ${target.position}")
SourceStorageKind.LITERALNUMBER -> {
val hex = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc #$${hex.substring(6, 8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc #$${hex.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc ${value.asmVarname}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc ${value.asmVarname}+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
// it's not an expression so no need to preserve R14:R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
asmgen.out("""
ldy #$offset
clc
lda ($zpPtrVar),y
adc cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
adc cx16.r14+3
sta ($zpPtrVar),y""")
}
else -> TODO("inplace long add with ${value.kind} ${target.position}")
}
}
private fun inplaceLongSub(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> TODO("inplace long sub with ${value.number} ${target.position}")
SourceStorageKind.VARIABLE -> TODO("inplace long sub with ${value.asmVarname} ${target.position}")
SourceStorageKind.EXPRESSION -> TODO("inplace long sub with ${value.expression} ${target.position}")
SourceStorageKind.LITERALNUMBER -> {
val hex = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc #$${hex.substring(6, 8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc #$${hex.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc ${value.asmVarname}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc ${value.asmVarname}+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
// it's not an expression so no need to preserve R14:R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, true)
asmgen.out("""
ldy #$offset
sec
lda ($zpPtrVar),y
sbc cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
sbc cx16.r14+3
sta ($zpPtrVar),y""")
}
else -> TODO("inplace long sub with ${value.kind} ${target.position}")
}
}
@@ -1621,7 +1787,220 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
}
}
fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
private fun inplaceLongAnd(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
and cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
and cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long &= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceLongOr(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
ora cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
ora cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long |= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
private fun inplaceLongXor(target: PtrTarget, value: AsmAssignSource) {
val (zpPtrVar, offset) = deref(target.pointer)
when(value.kind) {
SourceStorageKind.LITERALNUMBER -> {
val number = value.number!!.number.toLongHex()
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor #$${number.substring(6,8)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.substring(4, 6)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.substring(2, 4)}
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor #$${number.take(2)}
sta ($zpPtrVar),y""")
}
SourceStorageKind.VARIABLE -> {
require(value.datatype.isLong)
val varname = value.asmVarname
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor $varname
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor $varname+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.EXPRESSION -> {
require(value.datatype.isLong)
// not an expression so no need to preserve R14/R15
asmgen.assignExpressionToRegister(value.expression!!, RegisterOrPair.R14R15_32, target.dt.isSigned)
asmgen.out("""
ldy #$offset
lda ($zpPtrVar),y
eor cx16.r14
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+1
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+2
sta ($zpPtrVar),y
iny
lda ($zpPtrVar),y
eor cx16.r14+3
sta ($zpPtrVar),y""")
}
SourceStorageKind.REGISTER -> TODO("inplace long ^= register ${target.position}")
else -> throw AssemblyError("weird source value $value")
}
}
internal fun assignIndexedPointer(target: AsmAssignTarget, arrayVarName: String, index: PtExpression, arrayDt: DataType) {
TODO("assign indexed pointer from array $arrayVarName at ${target.position}")
// val ptrZp = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, target.scope, target.position, variableAsmName="P8ZP_SCRATCH_PTR")
// assignAddressOfIndexedPointer(ptrZp, arrayVarName, arrayDt, index)
@@ -1692,7 +2071,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
pha""")
}
in combinedLongRegisters -> {
TODO("save on stack long register pair")
TODO("save on stack long register pair - do we really want to do this?")
}
else -> asmgen.saveRegisterStack(regs.asCpuRegister(), false)
}
@@ -1727,7 +2106,7 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
sta $regname""")
}
in combinedLongRegisters -> {
TODO("restore from stack long register")
TODO("restore from stack long register - do we really want to do this?")
}
else -> asmgen.restoreRegisterStack(regs.asCpuRegister(), false)
}
@@ -123,6 +123,9 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
internal fun translate(augAssign: PtAugmentedAssign): IRCodeChunks {
if(augAssign.target.type.isString)
throw AssemblyError("cannot assign to str type ${augAssign.position}")
// augmented assignment always has just a single target
if (augAssign.target.children.single() is PtIrRegister)
throw AssemblyError("assigning to a register should be done by just evaluating the expression into resultregister")
@@ -467,7 +470,11 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
}
private fun translateRegularAssign(assignment: PtAssignment): IRCodeChunks {
// note: assigning array and string values is done via an explicit memcopy/stringcopy function call.
if(assignment.target.type.isString) {
// assigning array and string values is done via an explicit memcopy/stringcopy function calls.
throw AssemblyError("cannot assign to str type ${assignment.position}")
}
val valueDt = irType(assignment.value.type)
val targetDt = irType(assignment.target.type)
val result = mutableListOf<IRCodeChunkBase>()
@@ -17,7 +17,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
"abs__byte", "abs__word", "abs__long", "abs__float" -> funcAbs(call)
"cmp" -> funcCmp(call)
"sgn" -> funcSgn(call)
"sqrt__ubyte", "sqrt__uword", "sqrt__float" -> funcSqrt(call)
"sqrt__ubyte", "sqrt__uword", "sqrt__long", "sqrt__float" -> funcSqrt(call)
"divmod__ubyte" -> funcDivmod(call, IRDataType.BYTE)
"divmod__uword" -> funcDivmod(call, IRDataType.WORD)
"rsave", "rrestore" -> ExpressionCodeResult.EMPTY // vm doesn't have registers to save/restore
@@ -157,6 +157,7 @@ class IRCodeGen(
is IRCodeChunk -> {
val replacement = IRCodeChunk(sub.label, first.next)
replacement.instructions += first.instructions
replacement.appendSrcPositions(first.sourceLinesPositions)
replacement
}
is IRInlineAsmChunk -> IRInlineAsmChunk(sub.label, first.assembly, first.isIR, first.next)
@@ -410,6 +411,7 @@ class IRCodeGen(
is IRCodeChunk -> {
val newChunk = IRCodeChunk(label, first.next)
newChunk.instructions += first.instructions
newChunk.appendSrcPositions(first.sourceLinesPositions)
newChunk
}
is IRInlineAsmChunk -> {
@@ -122,7 +122,6 @@ private fun convert(variable: StMemVar): IRStMemVar {
private fun convert(constant: StConstant): IRStConstant {
val dt = DataType.forDt(constant.dt)
val scopedName = if('.' in constant.name) {
constant.name
} else {
@@ -132,7 +131,7 @@ private fun convert(constant: StConstant): IRStConstant {
constant.name
}
}
return IRStConstant(scopedName, dt, constant.value)
return IRStConstant(scopedName, constant.dt, constant.value)
}
@@ -244,8 +244,16 @@ class VarConstantValueTypeAdjuster(
dt.isUnsignedByte -> "sqrt__ubyte"
dt.isUnsignedWord -> "sqrt__uword"
dt.isFloat -> "sqrt__float"
dt.isLong -> {
val value = functionCallExpr.args[0].constValue(program)?.number
if(value!=null && value<0) {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
return noModifications
}
"sqrt__long"
}
else -> {
errors.err("expected unsigned or float numeric argument", functionCallExpr.args[0].position)
errors.err("expected numeric argument", functionCallExpr.args[0].position)
return noModifications
}
}
+12
View File
@@ -406,6 +406,7 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
%asm {{
jsr cbm.SCREEN
inx
txa
rts
}}
@@ -415,11 +416,22 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
%asm {{
jsr cbm.SCREEN
iny
tya
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr cbm.SCREEN
inx
iny
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+8
View File
@@ -424,6 +424,14 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+9 -4
View File
@@ -566,7 +566,7 @@ return_status:
sub internal_f_open_w(str filename, bool open_for_seeks) -> bool {
f_close_w()
list_filename = filename
void strings.copy(filename, list_filename)
str modifier = ",s,?"
modifier[3] = 'w'
if open_for_seeks
@@ -624,7 +624,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
@@ -818,8 +818,13 @@ io_error:
; For use directly after a load or load_raw call (don't mess with the ram bank yet):
; Calculates the number of bytes loaded (files > 64Kb are truncated to 16 bits)
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> uword {
return $2000 * (cx16.getrambank() - startbank) + endaddress - startaddress
sub load_size(ubyte startbank, uword startaddress, uword endaddress) -> long {
&long banksize = &cx16.r12
banksize = cx16.getrambank() - startbank
banksize <<= 13 ; * 8192
banksize += endaddress
banksize -= startaddress
return banksize
}
asmsub vload(str name @R0, ubyte bank @A, uword startaddress @R1) clobbers(X, Y) -> bool @A {
+7
View File
@@ -615,6 +615,13 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jmp cbm.SCREEN
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+11
View File
@@ -236,6 +236,17 @@ asmsub height() clobbers(X, Y) -> ubyte @A {
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
jsr width
tax
jsr height
tay
rts
}}
}
asmsub waitkey() -> ubyte @A {
%asm {{
- jsr cbm.GETIN
+19
View File
@@ -266,6 +266,25 @@ strcpy .proc
rts
.pend
strncpy .proc
; copy a string (must be 0-terminated) from A/Y to (P8ZP_SCRATCH_W1)
; with maximum length to copy in X.
; returns the length of the string that was copied in Y.
sta P8ZP_SCRATCH_W2
sty P8ZP_SCRATCH_W2+1
ldy #$ff
- iny
lda (P8ZP_SCRATCH_W2),y
sta (P8ZP_SCRATCH_W1),y
beq +
dex
bne -
iny
lda #0
sta (P8ZP_SCRATCH_W1),y
+ rts
.pend
strcmp_expression .proc
; -- compare strings, result in A
lda _arg_s2
+1 -1
View File
@@ -550,7 +550,7 @@ done:
return list_filename
io_error:
list_filename = "io error"
void strings.copy("io error", list_filename)
goto done
}
+47 -2
View File
@@ -213,8 +213,6 @@ _found tya
asmsub copy(str source @R0, str target @AY) clobbers(A) -> ubyte @Y {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
; Often you don’t have to call this explicitly and can just write string1 = string2
; but this function is useful if you’re dealing with addresses for instance.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
@@ -224,6 +222,18 @@ _found tya
}}
}
asmsub ncopy(str source @R0, str target @AY, ubyte maxlength @X) clobbers(A, X) -> ubyte @Y {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
%asm {{
sta P8ZP_SCRATCH_W1
sty P8ZP_SCRATCH_W1+1
lda cx16.r0
ldy cx16.r0+1
jmp prog8_lib.strncpy
}}
}
asmsub append(str target @R0, str suffix @R1) clobbers(Y) -> ubyte @A {
; Append the suffix string to the target. (make sure the buffer is large enough!)
; Returns the length of the resulting string.
@@ -249,6 +259,41 @@ _found tya
}}
}
asmsub nappend(str target @R0, str suffix @R1, ubyte maxlength @X) clobbers(Y) -> ubyte @A {
; Append the suffix string to the target. (make sure the buffer is large enough!)
; Returns the length of the resulting string.
%asm {{
lda cx16.r0
ldy cx16.r0+1
jsr length
sty P8ZP_SCRATCH_B1
cpx P8ZP_SCRATCH_B1
beq _max_too_small
bmi _max_too_small
txa
sec
sbc P8ZP_SCRATCH_B1
sta P8ZP_SCRATCH_B1
tya
clc
adc cx16.r0
sta P8ZP_SCRATCH_W1
lda cx16.r0+1
adc #0
sta P8ZP_SCRATCH_W1+1
lda cx16.r1
ldy cx16.r1+1
ldx P8ZP_SCRATCH_B1
jsr prog8_lib.strncpy
tya
clc
adc P8ZP_SCRATCH_B1
rts
_max_too_small
rts
}}
}
asmsub compare(str string1 @R0, str string2 @AY) clobbers(Y) -> byte @A {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
+3 -3
View File
@@ -206,7 +206,7 @@ diskio {
sub save(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{
load.b r99100,0
load.b r99100,#0
loadm.w r99000,diskio.save.filenameptr
loadm.w r99001,diskio.save.start_address
loadm.w r99002,diskio.save.savesize
@@ -218,7 +218,7 @@ diskio {
; like save() but omits the 2 byte prg header.
sub save_raw(str filenameptr, uword start_address, uword savesize) -> bool {
%ir {{
load.b r99100,1
load.b r99100,#1
loadm.w r99000,diskio.save_raw.filenameptr
loadm.w r99001,diskio.save_raw.start_address
loadm.w r99002,diskio.save_raw.savesize
@@ -284,7 +284,7 @@ diskio {
; get the load adress from a PRG file (usually $0801 but it can be different)
if f_open(filename) {
uword address
f_read(&address, 2)
void f_read(&address, 2)
f_close()
return address
}
+24 -3
View File
@@ -106,12 +106,23 @@ strings {
sub copy(str source, str target) -> ubyte {
; Copy a string to another, overwriting that one.
; Returns the length of the string that was copied.
; Often you don’t have to call this explicitly and can just write string1 = string2
; but this function is useful if you’re dealing with addresses for instance.
%ir {{
loadm.w r99000,strings.copy.source
loadm.w r99001,strings.copy.target
syscall 39 (r99000.w, r99001.w): r99100.b
load.b r99100,#255
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
returnr.b r99100
}}
}
sub ncopy(str source, str target, ubyte maxlength) -> ubyte {
; Copy a string to another, overwriting that one, but limited to the given length.
; Returns the length of the string that was copied.
%ir {{
loadm.w r99000,strings.ncopy.source
loadm.w r99001,strings.ncopy.target
loadm.b r99100,strings.ncopy.maxlength
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
returnr.b r99100
}}
}
@@ -123,6 +134,16 @@ strings {
return copy(suffix, target+cx16.r0L) + cx16.r0L
}
sub nappend(str target, str suffix, ubyte maxlength) -> ubyte {
; Append the suffix string to the target, up to the given maximum length of the combined string.
; Returns the length of the resulting string.
cx16.r0L = length(target)
if maxlength<cx16.r0L
return cx16.r0L
maxlength -= cx16.r0L
return ncopy(suffix, target+cx16.r0L, maxlength) + cx16.r0L
}
sub compare(str st1, str st2) -> byte {
; Compares two strings for sorting.
; Returns -1 (255), 0 or 1, meaning: string1 sorts before, equal or after string2.
+2 -1
View File
@@ -53,7 +53,8 @@ sys {
%ir {{
loadm.w r99000,sys.internal_stringcopy.source
loadm.w r99001,sys.internal_stringcopy.tgt
syscall 39 (r99000.w, r99001.w): r99100.b
load.b r99100,#255
syscall 39 (r99000.w, r99001.w, r99100.b): r99100.b
}}
}
+6 -1
View File
@@ -21,6 +21,11 @@ sub height() -> ubyte {
}}
}
sub size() -> ubyte, ubyte {
; -- returns the text screen width and height (number of columns and rows)
return width(), height()
}
sub clear_screen() {
str @shared sequence = "\x1b[2J\x1B[H"
%ir {{
@@ -171,7 +176,7 @@ sub input_chars (str buffer) -> ubyte {
; It assumes the keyboard is selected as I/O channel!
%ir {{
loadm.w r99000,txt.input_chars.buffer
load.b r99100,80
load.b r99100,#80
syscall 6 (r99000.w, r99100.b): r99100.b
returnr.b r99100
}}
@@ -20,7 +20,19 @@ internal val constEvaluatorsForBuiltinFuncs: Map<String, ConstExpressionCaller>
"sgn" to ::builtinSgn,
"sqrt__ubyte" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__uword" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) { sqrt(it.toDouble()) } },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) { sqrt(it) } },
"sqrt__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, false) {
val value=it.toDouble()
if(value<0)
throw CannotEvaluateException("sqrt", "argument cannot be negative")
else
sqrt(value)
} },
"sqrt__float" to { a, p, prg -> oneFloatArgOutputFloat(a, p, prg) {
if(it<0)
throw CannotEvaluateException("sqrt", "argument cannot be negative")
else
sqrt(it)
} },
"lsb" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
"lsb__long" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 255).toDouble() } },
"lsw" to { a, p, prg -> oneIntArgOutputInt(a, p, prg, true) { x: Int -> (x and 65535).toDouble() } },
@@ -71,11 +83,14 @@ private fun oneIntArgOutputInt(args: List<Expression>, position: Position, progr
if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position)
} else {
if(constval.type!=BaseDataType.UBYTE && constval.type!=BaseDataType.UWORD)
if(!constval.type.isInteger)
throw SyntaxError("built-in function requires one integer argument", position)
}
val integer = constval.number.toInt()
return NumericLiteral.optimalInteger(function(integer).toInt(), args[0].position)
val result = function(integer)
if(result.isNaN())
throw CannotEvaluateException("built-in function", "result is NaN $position")
return NumericLiteral.optimalInteger(result.toInt(), args[0].position)
}
private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Double): NumericLiteral {
@@ -84,8 +99,10 @@ private fun oneFloatArgOutputFloat(args: List<Expression>, position: Position, p
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
if(constval.type != BaseDataType.FLOAT)
throw SyntaxError("built-in function requires one float argument", position)
return NumericLiteral(BaseDataType.FLOAT, function(constval.number), args[0].position)
val result = function(constval.number)
if(result.isNaN())
throw CannotEvaluateException("built-in function", "result is NaN $position")
return NumericLiteral(BaseDataType.FLOAT, result, args[0].position)
}
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteral {
@@ -138,7 +155,14 @@ private fun builtinSizeof(args: List<Expression>, position: Position, program: P
val elementDt = dt.getOrUndef().elementType()
NumericLiteral.optimalInteger(program.memsizer.memorySize(elementDt, length), position)
}
dt.isString -> throw SyntaxError("sizeof(str) is undefined, did you mean len, or perhaps strings.length?", position)
dt.isString -> {
if(target is VarDecl) {
val length = (target.value as StringLiteral).value.length + 1 // add the closing 0-byte
NumericLiteral(BaseDataType.UBYTE, length.toDouble(), position)
}
else
throw SyntaxError("sizeof(str) is undefined here. Perhaps use len, or strings.length?", position)
}
else -> NumericLiteral.optimalInteger( program.memsizer.memorySize(dt.getOrUndef(), null), position)
}
} else {
@@ -50,8 +50,8 @@ class ModuleImporter(private val program: Program,
fun importImplicitLibraryModule(name: String): Module? {
val import = Directive("%import", listOf(
DirectiveArg(name, 42u, position = Position("<<<implicit-import>>>", 0, 0, 0))
), Position("<<<implicit-import>>>", 0, 0, 0))
DirectiveArg(name, 42u, position = Position("~implicit-import~", 0, 0, 0))
), Position("~implicit-import~", 0, 0, 0))
return executeImportDirective(import, null)
}
@@ -651,6 +651,14 @@ internal class AstChecker(private val program: Program,
}
override fun visit(assignment: Assignment) {
if(assignment.target.inferType(program).isString) {
if(assignment.isAugmentable)
errors.err("cannot assign to string by value (use strings.(n)copy or strings.(n)append)", assignment.position)
else
errors.err("cannot assign to string by value (use strings.(n)copy)", assignment.position)
return
}
if(assignment.target.multi==null) {
val targetDt = assignment.target.inferType(program)
val valueDt = assignment.value.inferType(program)
@@ -874,9 +882,9 @@ internal class AstChecker(private val program: Program,
err("recursive var declaration")
// CONST can only occur on simple types (byte, word, float)
if(decl.type== VarDeclType.CONST) {
if(decl.type==VarDeclType.CONST) {
if (!decl.datatype.isNumericOrBool)
err("const can only be used on numeric types or booleans")
err("const can only be used on numbers and booleans")
}
// FLOATS enabled?
@@ -1500,8 +1508,12 @@ internal class AstChecker(private val program: Program,
}
if(expr.operator=="+" || expr.operator=="-") {
if(leftDt.isString || rightDt.isString || leftDt.isArray || rightDt.isArray) {
errors.err("missing & (address-of) on the operand", expr.position)
if(leftDt.isString || leftDt.isArray) {
errors.err("missing & (address-of) on the operand", expr.left.position)
return
}
if(rightDt.isString || rightDt.isArray) {
errors.err("missing & (address-of) on the operand", expr.right.position)
return
}
}
@@ -1950,7 +1962,7 @@ internal class AstChecker(private val program: Program,
} else if (target.datatype.isString) {
if (target.value is StringLiteral) {
// check string lengths for non-memory mapped strings
val stringLen = (target.value as StringLiteral).value.length
val stringLen = (target.value as StringLiteral).value.length + 1 // include the 0-byte terminator
if (index != null && index !in 0..<stringLen)
errors.err("index out of bounds", arrayIndexedExpression.indexer.position)
}
@@ -34,8 +34,14 @@ internal fun Program.reorderStatements(options: CompilationOptions, errors: IErr
internal fun Program.changeNotExpressionAndIfComparisonExpr(errors: IErrorReporter, target: ICompilationTarget) {
val changer = NotExpressionAndIfComparisonExprChanger(this, errors, target)
changer.visit(this)
while(errors.noErrors() && changer.applyModifications()>0) {
changer.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && changer.applyModifications() > 0)
changer.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("changeNotExpressionAndIfComparisonExpr() is looping endlessly")
}
}
@@ -93,8 +99,15 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
val desugar = CodeDesugarer(this, options.compTarget, errors)
desugar.visit(this)
while(errors.noErrors() && desugar.applyModifications()>0)
desugar.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && desugar.applyModifications() > 0)
desugar.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("desugaring() is looping endlessly")
}
}
internal fun Program.verifyFunctionArgTypes(errors: IErrorReporter, options: CompilationOptions) {
@@ -121,16 +134,30 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
if(errors.noErrors()) {
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
lit2decl.visit(this)
while(errors.noErrors() && lit2decl.applyModifications()>0)
lit2decl.visit(this)
for(numCycles in 0..1000) {
if(errors.noErrors() && lit2decl.applyModifications() > 0)
lit2decl.visit(this)
else
break
if(numCycles==1000) {
throw InternalCompilerException("checkIdentifiers() is looping endlessly - check for circular aliases")
}
}
}
}
internal fun Program.variousCleanups(errors: IErrorReporter, options: CompilationOptions) {
val process = VariousCleanups(this, errors, options)
process.visit(this)
while(errors.noErrors() && process.applyModifications()>0) {
process.visit(this)
for(numCycles in 0..2000) {
if (errors.noErrors() && process.applyModifications() > 0)
process.visit(this)
else
break
if(numCycles==2000)
throw InternalCompilerException("variousCleanups() is looping endlessly")
}
}
@@ -44,6 +44,9 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
override fun visit(alias: Alias) {
if(alias.target.targetStatement(program.builtinFunctions)==null)
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
if(alias.alias == alias.target.nameInSource.first())
errors.err("alias references itself", alias.position)
}
override fun visit(block: Block) {
@@ -96,6 +99,50 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
super.visit(decl)
}
private val keywords = setOf(
"void",
"on",
"in",
"to",
"if",
"downto",
"else",
"step",
"call",
"goto",
"inline",
"struct",
"then",
"alias",
"defer",
"const",
"sizeof",
"as",
"return",
"break",
"continue",
"true",
"false",
"sub",
"asmsub",
"extsub",
"clobbers",
"while",
"do",
"until",
"repeat",
"unroll",
"when"
)
override fun visit(struct: StructDecl) {
if(struct.name in BuiltinFunctions)
errors.err("builtin function cannot be redefined", struct.position)
else if(struct.name in keywords)
errors.err("struct name cannot be a keyword", struct.position)
super.visit(struct)
}
override fun visit(subroutine: Subroutine) {
if(subroutine.name in BuiltinFunctions) {
// the builtin functions can't be redefined
@@ -56,7 +56,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
if(subroutine.returntypes.isNotEmpty())
errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
else {
val returnStmt = Return(arrayOf(), subroutine.position)
val returnStmt = Return(arrayOf(), Position.DUMMY)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
} else {
@@ -68,7 +68,7 @@ internal class BeforeAsmAstChanger(val program: Program, private val options: Co
// .... we cannot return this as an error, because that also breaks legitimate cases where the return is done from within a nested scope somewhere
// errors.err("subroutine is missing a return statement with value(s)", subroutine.position)
} else {
val returnStmt = Return(arrayOf(), subroutine.position)
val returnStmt = Return(arrayOf(), Position.DUMMY)
mods += IAstModification.InsertLast(returnStmt, subroutine)
}
}
@@ -621,10 +621,9 @@ _after:
else
mutableListOf(conditionVar, assignIndex!!, ifSt)
, ongoto.position)
val declscope = parent.definingScope
return listOf(
IAstModification.ReplaceNode(ongoto, replacementScope, parent),
IAstModification.InsertFirst(jumplistArray, declscope)
IAstModification.InsertFirst(jumplistArray, ongoto.definingScope)
)
}
@@ -298,15 +298,6 @@ internal class StatementReorderer(
checkCopyArrayValue(assignment)
}
if(!assignment.isAugmentable) {
if (targetType issimpletype BaseDataType.STR) {
if (valueType issimpletype BaseDataType.STR || valueType.getOrUndef() == DataType.pointer(BaseDataType.UBYTE)) {
// replace string assignment by a call to stringcopy
return copyStringValue(assignment)
}
}
}
return noModifications
}
@@ -372,19 +363,6 @@ internal class StatementReorderer(
}
}
private fun copyStringValue(assign: Assignment): List<IAstModification> {
val identifier = assign.target.identifier!!
val strcopy = FunctionCallStatement(IdentifierReference(listOf("sys", "internal_stringcopy"), assign.position),
mutableListOf(
assign.value as? IdentifierReference ?: assign.value,
identifier
),
false,
assign.position
)
return listOf(IAstModification.ReplaceNode(assign, strcopy, assign.parent))
}
override fun after(deref: ArrayIndexedPtrDereference, parent: Node): Iterable<IAstModification> {
if(parent is AssignTarget) {
val zeroIndexer = deref.chain.firstOrNull { it.second?.constIndex()==0 }
@@ -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),
@@ -27,45 +27,46 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
if(valueType.isUnknown)
return noModifications
val valueDt = valueType.getOrUndef()
when(decl.type) {
VarDeclType.VAR -> {
if(decl.isArray) {
// using a array of words as initializer to a pointer array is fine
if (!valueDt.isSplitWordArray || !decl.datatype.isPointerArray)
errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position)
} else if(!decl.datatype.isString) {
if (valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value?.constValue(program)
if (constValue != null)
errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position)
else
errors.err("value out of range for ${decl.datatype}", decl.value!!.position)
if(!(valueDt.isWord && decl.datatype.isPointer)) {
when(decl.type) {
VarDeclType.VAR -> {
if(decl.isArray) {
// using a array of words as initializer to a pointer array is fine
if (!valueDt.isSplitWordArray || !decl.datatype.isPointerArray)
errors.err("value has incompatible type ($valueType) for the variable (${decl.datatype})", decl.value!!.position)
} else if(!decl.datatype.isString) {
if (valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value?.constValue(program)
if (constValue != null)
errors.err("value '$constValue' out of range for ${decl.datatype}", constValue.position)
else
errors.err("value out of range for ${decl.datatype}", decl.value!!.position)
}
}
}
}
VarDeclType.CONST -> {
// change the vardecl type itself as well, but only if new type is smaller
if(valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value!!.constValue(program)!!
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
} else {
// don't make it signed if it was unsigned and vice versa, except when it is a long const declaration
if(!decl.datatype.isLong &&
(valueDt.isSigned && decl.datatype.isUnsigned ||
valueDt.isUnsigned && decl.datatype.isSigned))
{
VarDeclType.CONST -> {
// change the vardecl type itself as well, but only if new type is smaller
if(valueDt.largerSizeThan(decl.datatype)) {
val constValue = decl.value!!.constValue(program)!!
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
} else {
val changed = decl.copy(valueDt)
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
// don't make it signed if it was unsigned and vice versa, except when it is a long const declaration
if(!decl.datatype.isLong &&
(valueDt.isSigned && decl.datatype.isUnsigned ||
valueDt.isUnsigned && decl.datatype.isSigned))
{
val constValue = decl.value!!.constValue(program)!!
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
} else {
val changed = decl.copy(valueDt)
return listOf(IAstModification.ReplaceNode(decl, changed, parent))
}
}
}
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
throw FatalAstException("value type for a memory var should be word or byte (address)")
}
VarDeclType.MEMORY -> if(!valueType.isWords && !valueType.isBytes)
throw FatalAstException("value type for a memory var should be word or byte (address)")
}
}
// check splitting of word arrays
@@ -431,6 +432,16 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
val newIndex = NumericLiteral.optimalNumeric(arraysize+index, arrayIndexedExpression.indexer.position)
arrayIndexedExpression.indexer.indexExpr = newIndex
newIndex.linkParents(arrayIndexedExpression.indexer)
} else if(target?.datatype?.isString==true) {
val stringsize = (target.value as StringLiteral).value.length
if(stringsize+index < 0) {
errors.err("index out of bounds", arrayIndexedExpression.position)
return noModifications
}
// replace the negative index by the normal index
val newIndex = NumericLiteral.optimalNumeric(stringsize+index, arrayIndexedExpression.indexer.position)
arrayIndexedExpression.indexer.indexExpr = newIndex
newIndex.linkParents(arrayIndexedExpression.indexer)
}
} else if(arrayIndexedExpression.pointerderef!=null) {
TODO("cleanup pointer indexing ${arrayIndexedExpression.position}")
+8 -8
View File
@@ -149,8 +149,8 @@ class TestModuleImporter: FunSpec({
shouldThrow<ParseError> { act() }.let {
it.position.file shouldBe SourceCode.relative(srcPath).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
@@ -169,8 +169,8 @@ class TestModuleImporter: FunSpec({
shouldThrow<ParseError> { act() }.let {
it.position.file shouldBe SourceCode.relative(imported).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
@@ -230,8 +230,8 @@ class TestModuleImporter: FunSpec({
importer.importImplicitLibraryModule(srcPath.nameWithoutExtension) }.let {
it.position.file shouldBe SourceCode.relative(srcPath).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
program.modules.size shouldBe PROG8_CONTAINER_MODULES.size
@@ -252,8 +252,8 @@ class TestModuleImporter: FunSpec({
act() }.let {
it.position.file shouldBe SourceCode.relative(imported).toString()
withClue("line; should be 1-based") { it.position.line shouldBe 2 }
withClue("startCol; should be 0-based") { it.position.startCol shouldBe 4 }
withClue("endCol; should be 0-based") { it.position.endCol shouldBe 6 }
withClue("startCol; should be 1-based") { it.position.startCol shouldBe 5 }
withClue("endCol; should be 1-based") { it.position.endCol shouldBe 7 }
}
}
withClue("imported module with error in it should not be present") { program.modules.size shouldBe PROG8_CONTAINER_MODULES.size }
+84 -86
View File
@@ -23,10 +23,8 @@ class TestNumericLiteral: FunSpec({
return lv1.type==lv2.type && lv1==lv2
}
val dummyPos = Position("test", 0, 0, 0)
test("testIdentity") {
val v = NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)
val v = NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)
(v==v) shouldBe true
(v != v) shouldBe false
(v <= v) shouldBe true
@@ -34,124 +32,124 @@ class TestNumericLiteral: FunSpec({
(v < v ) shouldBe false
(v > v ) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
}
test("test truncating avoidance") {
shouldThrow<ExpressionError> {
NumericLiteral(BaseDataType.BYTE, -2.345, dummyPos)
NumericLiteral(BaseDataType.BYTE, -2.345, Position.DUMMY)
}.message shouldContain "float value given for integer"
shouldThrow<ExpressionError> {
NumericLiteral(BaseDataType.BYTE, -2.6, dummyPos)
NumericLiteral(BaseDataType.BYTE, -2.6, Position.DUMMY)
}.message shouldContain "float value given for integer"
shouldThrow<ExpressionError> {
NumericLiteral(BaseDataType.UWORD, 2222.345, dummyPos)
NumericLiteral(BaseDataType.UWORD, 2222.345, Position.DUMMY)
}.message shouldContain "float value given for integer"
NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
NumericLiteral(BaseDataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
NumericLiteral(BaseDataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
NumericLiteral(BaseDataType.FLOAT, 123.456, dummyPos)
NumericLiteral(BaseDataType.UBYTE, 2.0, Position.DUMMY).number shouldBe 2.0
NumericLiteral(BaseDataType.BYTE, -2.0, Position.DUMMY).number shouldBe -2.0
NumericLiteral(BaseDataType.UWORD, 2222.0, Position.DUMMY).number shouldBe 2222.0
NumericLiteral(BaseDataType.FLOAT, 123.456, Position.DUMMY)
}
test("testEqualsAndNotEquals") {
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) == NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) == NumericLiteral(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) == NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) == NumericLiteral(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) == NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) == NumericLiteral(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) == NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 22239.0, dummyPos) == NumericLiteral(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) == NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) == NumericLiteral(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) == NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) == NumericLiteral(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) == NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 22239.0, Position.DUMMY) == NumericLiteral(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) == NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 22239.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 22239.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) != NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) != NumericLiteral(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) != NumericLiteral(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 245.0, dummyPos) != NumericLiteral(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) != NumericLiteral(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos) != NumericLiteral(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) != NumericLiteral(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) != NumericLiteral(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos) != NumericLiteral(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) != NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) != NumericLiteral(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) != NumericLiteral(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 245.0, Position.DUMMY) != NumericLiteral(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) != NumericLiteral(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY) != NumericLiteral(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) != NumericLiteral(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) != NumericLiteral(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY) != NumericLiteral(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe true
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 245.0, dummyPos), NumericLiteral(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, dummyPos), NumericLiteral(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, dummyPos), NumericLiteral(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 245.0, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.UWORD, 12345.0, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(NumericLiteral(BaseDataType.FLOAT, 9.99, Position.DUMMY), NumericLiteral(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe false
}
test("testEqualsRef") {
(StringLiteral.create("hello", Encoding.PETSCII, dummyPos) == StringLiteral.create("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.PETSCII, dummyPos) != StringLiteral.create("bye", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos) == StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos) != StringLiteral.create("bye", Encoding.SCREENCODES, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, dummyPos) != StringLiteral.create("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY) == StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY) != StringLiteral.create("bye", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY) == StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY) != StringLiteral.create("bye", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(StringLiteral.create("hello", Encoding.SCREENCODES, Position.DUMMY) != StringLiteral.create("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
val lvOne = NumericLiteral(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwo = NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThree = NumericLiteral(BaseDataType.UBYTE, 3.0, dummyPos)
val lvOneR = NumericLiteral(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwoR = NumericLiteral(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThreeR = NumericLiteral(BaseDataType.UBYTE, 3.0, dummyPos)
val lvFour= NumericLiteral(BaseDataType.UBYTE, 4.0, dummyPos)
val lv1 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOne, lvTwo, lvThree), dummyPos)
val lv2 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvThreeR), dummyPos)
val lv3 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvFour), dummyPos)
val lvOne = NumericLiteral(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwo = NumericLiteral(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThree = NumericLiteral(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvOneR = NumericLiteral(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwoR = NumericLiteral(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThreeR = NumericLiteral(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvFour= NumericLiteral(BaseDataType.UBYTE, 4.0, Position.DUMMY)
val lv1 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOne, lvTwo, lvThree), Position.DUMMY)
val lv2 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvThreeR), Position.DUMMY)
val lv3 = ArrayLiteral(InferredTypes.InferredType.known(DataType.arrayFor(BaseDataType.UBYTE)), arrayOf(lvOneR, lvTwoR, lvFour), Position.DUMMY)
lv1 shouldBe lv2
lv1 shouldNotBe lv3
}
test("testGreaterThan") {
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) > NumericLiteral(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) > NumericLiteral(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) > NumericLiteral(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) > NumericLiteral(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) >= NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) >= NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) >= NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) > NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) > NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) > NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) > NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) > NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) >= NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) >= NumericLiteral(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) >= NumericLiteral(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) >= NumericLiteral(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= NumericLiteral(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe false
}
test("testLessThan") {
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) < NumericLiteral(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) < NumericLiteral(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) < NumericLiteral(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) < NumericLiteral(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) <= NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) <= NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) <= NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) < NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) < NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) < NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) < NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) < NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, dummyPos) <= NumericLiteral(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, dummyPos) <= NumericLiteral(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, dummyPos) <= NumericLiteral(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe false
(NumericLiteral(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.UWORD, 254.0, Position.DUMMY) <= NumericLiteral(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe false
(NumericLiteral(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= NumericLiteral(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe false
}
test("optimalInteger") {
+17 -10
View File
@@ -1001,7 +1001,6 @@ main {
test("no operand swap on logical expressions with shortcircuit evaluation") {
val src="""
%import diskio
%zeropage basicsafe
%option no_sysinit
@@ -1009,15 +1008,23 @@ main {
str scanline_buf = "?"* 20
sub start() {
if diskio.f_open("test.prg") and diskio.f_read(scanline_buf, 2)==2
if f_open("test.prg") and f_read(scanline_buf, 2)==2
cx16.r0++
if diskio.f_open("test.prg") or diskio.f_read(scanline_buf, 2)==2
if f_open("test.prg") or f_read(scanline_buf, 2)==2
cx16.r0++
if diskio.f_open("test.prg") xor diskio.f_read(scanline_buf, 2)==2
if f_open("test.prg") xor f_read(scanline_buf, 2)==2
cx16.r0++
}
sub f_open(str name) -> bool {
return cx16.r0==99
}
sub f_read(str buf, uword lengthy) -> uword {
return cx16.r0
}
}"""
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = false)!!
val st = result.compilerAst.entrypoint.statements
@@ -1025,15 +1032,15 @@ main {
val ifCond1 = (st[0] as IfElse).condition as BinaryExpression
val ifCond2 = (st[1] as IfElse).condition as BinaryExpression
val ifCond3 = (st[2] as IfElse).condition as BinaryExpression
(ifCond1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_open")
(ifCond2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_open")
(ifCond3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_open")
(ifCond1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_open")
(ifCond2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_open")
(ifCond3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_open")
val right1 = ifCond1.right as BinaryExpression
val right2 = ifCond2.right as BinaryExpression
val right3 = ifCond3.right as BinaryExpression
(right1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_read")
(right2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_read")
(right3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("diskio", "f_read")
(right1.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_read")
(right2.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_read")
(right3.left as FunctionCallExpression).target.nameInSource shouldBe listOf("f_read")
}
test("eliminate same target register assignments") {
+87 -5
View File
@@ -662,6 +662,58 @@ main {
err[0] shouldContain("15:16: incompatible value type, can only assign uword or correct pointer")
}
xtest("pointer uword assignments with cast") {
val src="""
main {
struct Node1 {
ubyte type
word ww
}
struct Node2 {
ubyte type
^^ubyte text
}
sub start() {
^^Node1 @shared next
^^Node2 @shared this
^^ubyte ubptr
ubptr = next as ^^ubyte
ubptr = 12345
ubptr = cx16.r0
ubptr = next as uword ; TODO fix type error; the cast should succeed
this.text = next as ^^ubyte
this.text = 12345
this.text = cx16.r0
this.text = next as uword ; TODO fix type error; the cast should succeed
}
}"""
compileText(VMTarget(), false, src, outputDir) shouldNotBe null
compileText(C64Target(), false, src, outputDir) shouldNotBe null
}
test("const pointer gives proper error") {
val src="""
main {
sub start() {
^^uword @shared ptr = 7000
const ^^uword cptr = 8000
;ptr^^ = 12345
;cptr^^ = 12345
cx16.r0 = cptr
cx16.r1 = ptr
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = true) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain("const can only be used on numbers and booleans")
}
test("unknown field") {
val src="""
main {
@@ -1258,7 +1310,7 @@ main {
errors.errors[3] shouldContain "assigning this value to struct instance not supported"
}
xtest("assigning struct instances") {
test("assigning struct instances") {
val src="""
main {
sub start() {
@@ -1273,16 +1325,26 @@ main {
lp2^^ = lp1^^ ; memcopy(lp1, lp2, 11)
lp2[2] = lp1^^ ; memcopy(lp1, lp2 + 22, 11)
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above) TODO fix astchecker to allow this case
lp2[2]^^ = lp1^^ ; memcopy(lp1, lp2 + 22, 11) (same as above)
lp2^^ = lp1[2] ; memcopy(lp1 + 22, lp2, 11)
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above) TODO fix astchecker to allow this case
lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
lp2^^ = lp1[2]^^ ; memcopy(lp1 + 22, lp2, 11) (same as above)
;;; lp2[3] = lp1[2] ; memcopy(lp1 + 22, lp2 + 33, 11) TODO fix astchecker to allow this case
}
}"""
val errors = ErrorReporterForTests()
val result = compileText(VMTarget(), false, src, outputDir, errors=errors)!!
val st = result.compilerAst.entrypoint.statements
st.size shouldBe 99
st.size shouldBe 11
val memcopy1 = st[5] as FunctionCallStatement
val memcopy2 = st[6] as FunctionCallStatement
val memcopy3 = st[7] as FunctionCallStatement
val memcopy4 = st[8] as FunctionCallStatement
val memcopy5 = st[9] as FunctionCallStatement
memcopy1.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy2.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy3.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy4.target.nameInSource shouldBe listOf("sys", "memcopy")
memcopy5.target.nameInSource shouldBe listOf("sys", "memcopy")
}
test("a.b.c[i]^^.value as expression where pointer is struct") {
@@ -2548,4 +2610,24 @@ main {
be6.right shouldBe instanceOf<IdentifierReference>()
(a7.value as? ArrayIndexedExpression)?.indexer?.constIndex() shouldBe 0
}
test("long pointer assignments") {
val src="""
main {
sub start() {
long lvar = 999999
^^long lptr = 5000
pokel(5000, 11223344)
lptr^^ = 0
lptr^^ = lvar
lptr^^ = 82348234+lvar
lvar = lptr^^+1111111
}
}"""
compileText(VMTarget(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
compileText(C64Target(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
})
+84 -86
View File
@@ -20,10 +20,8 @@ class TestPtNumber: FunSpec({
return lv1.type==lv2.type && lv1==lv2
}
val dummyPos = Position("test", 0, 0, 0)
test("testIdentity") {
val v = PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)
val v = PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)
(v==v) shouldBe true
(v != v) shouldBe false
(v <= v) shouldBe true
@@ -31,127 +29,127 @@ class TestPtNumber: FunSpec({
(v < v ) shouldBe false
(v > v ) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
}
test("test truncating") {
shouldThrow<IllegalArgumentException> {
PtNumber(BaseDataType.BYTE, -2.345, dummyPos)
PtNumber(BaseDataType.BYTE, -2.345, Position.DUMMY)
}.message shouldContain "refused truncating"
shouldThrow<IllegalArgumentException> {
PtNumber(BaseDataType.BYTE, -2.6, dummyPos)
PtNumber(BaseDataType.BYTE, -2.6, Position.DUMMY)
}.message shouldContain "refused truncating"
shouldThrow<IllegalArgumentException> {
PtNumber(BaseDataType.UWORD, 2222.345, dummyPos)
PtNumber(BaseDataType.UWORD, 2222.345, Position.DUMMY)
}.message shouldContain "refused truncating"
PtNumber(BaseDataType.UBYTE, 2.0, dummyPos).number shouldBe 2.0
PtNumber(BaseDataType.BYTE, -2.0, dummyPos).number shouldBe -2.0
PtNumber(BaseDataType.UWORD, 2222.0, dummyPos).number shouldBe 2222.0
PtNumber(BaseDataType.FLOAT, 123.456, dummyPos)
PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY).number shouldBe 2.0
PtNumber(BaseDataType.BYTE, -2.0, Position.DUMMY).number shouldBe -2.0
PtNumber(BaseDataType.UWORD, 2222.0, Position.DUMMY).number shouldBe 2222.0
PtNumber(BaseDataType.FLOAT, 123.456, Position.DUMMY)
}
test("testEqualsAndNotEquals") {
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) == PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) == PtNumber(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) == PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) == PtNumber(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) == PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) == PtNumber(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) == PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 22239.0, dummyPos) == PtNumber(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) == PtNumber(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) == PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) == PtNumber(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) == PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) == PtNumber(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) == PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) == PtNumber(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) == PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 22239.0, Position.DUMMY) == PtNumber(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) == PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UWORD, 100.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 254.0, dummyPos), PtNumber(BaseDataType.UBYTE, 254.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.UWORD, 12345.0, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.FLOAT, 12345.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos), PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 22239.0, dummyPos), PtNumber(BaseDataType.UWORD, 22239.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.FLOAT, 9.99, dummyPos)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 254.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 12345.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 22239.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 22239.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) != PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) != PtNumber(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) != PtNumber(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 245.0, dummyPos) != PtNumber(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) != PtNumber(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos) != PtNumber(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) != PtNumber(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) != PtNumber(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos) != PtNumber(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) != PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) != PtNumber(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) != PtNumber(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 245.0, Position.DUMMY) != PtNumber(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) != PtNumber(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY) != PtNumber(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) != PtNumber(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) != PtNumber(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY) != PtNumber(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe true
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.UWORD, 101.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos), PtNumber(BaseDataType.FLOAT, 101.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 245.0, dummyPos), PtNumber(BaseDataType.UBYTE, 246.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.UWORD, 12346.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, dummyPos), PtNumber(BaseDataType.FLOAT, 12346.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.UBYTE, 9.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.UWORD, 9.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, dummyPos), PtNumber(BaseDataType.FLOAT, 9.0, dummyPos)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 101.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 245.0, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 246.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.UWORD, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.UWORD, 12345.0, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 12346.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.UBYTE, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.UWORD, 9.0, Position.DUMMY)) shouldBe false
sameValueAndType(PtNumber(BaseDataType.FLOAT, 9.99, Position.DUMMY), PtNumber(BaseDataType.FLOAT, 9.0, Position.DUMMY)) shouldBe false
}
test("testEqualsRef") {
(PtString("hello", Encoding.PETSCII, dummyPos) == PtString("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(PtString("hello", Encoding.PETSCII, dummyPos) != PtString("bye", Encoding.PETSCII, dummyPos)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, dummyPos) == PtString("hello", Encoding.SCREENCODES, dummyPos)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, dummyPos) != PtString("bye", Encoding.SCREENCODES, dummyPos)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, dummyPos) != PtString("hello", Encoding.PETSCII, dummyPos)) shouldBe true
(PtString("hello", Encoding.PETSCII, Position.DUMMY) == PtString("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.PETSCII, Position.DUMMY) != PtString("bye", Encoding.PETSCII, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, Position.DUMMY) == PtString("hello", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, Position.DUMMY) != PtString("bye", Encoding.SCREENCODES, Position.DUMMY)) shouldBe true
(PtString("hello", Encoding.SCREENCODES, Position.DUMMY) != PtString("hello", Encoding.PETSCII, Position.DUMMY)) shouldBe true
val lvOne = PtNumber(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwo = PtNumber(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThree = PtNumber(BaseDataType.UBYTE, 3.0, dummyPos)
val lvOneR = PtNumber(BaseDataType.UBYTE, 1.0, dummyPos)
val lvTwoR = PtNumber(BaseDataType.UBYTE, 2.0, dummyPos)
val lvThreeR = PtNumber(BaseDataType.UBYTE, 3.0, dummyPos)
val lvFour= PtNumber(BaseDataType.UBYTE, 4.0, dummyPos)
val lv1 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), dummyPos)
val lvOne = PtNumber(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwo = PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThree = PtNumber(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvOneR = PtNumber(BaseDataType.UBYTE, 1.0, Position.DUMMY)
val lvTwoR = PtNumber(BaseDataType.UBYTE, 2.0, Position.DUMMY)
val lvThreeR = PtNumber(BaseDataType.UBYTE, 3.0, Position.DUMMY)
val lvFour= PtNumber(BaseDataType.UBYTE, 4.0, Position.DUMMY)
val lv1 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
arrayOf(lvOne, lvTwo, lvThree).forEach { lv1.add(it) }
val lv2 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), dummyPos)
val lv2 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
arrayOf(lvOneR, lvTwoR, lvThreeR).forEach { lv2.add(it) }
val lv3 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), dummyPos)
val lv3 = PtArray(DataType.arrayFor(BaseDataType.UBYTE), Position.DUMMY)
arrayOf(lvOneR, lvTwoR, lvFour).forEach { lv3.add(it) }
lv1 shouldBe lv2
lv1 shouldNotBe lv3
}
test("testGreaterThan") {
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) > PtNumber(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) > PtNumber(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) > PtNumber(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) > PtNumber(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) > PtNumber(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) > PtNumber(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) >= PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) >= PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) >= PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) >= PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) > PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) > PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) > PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) > PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) > PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) > PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) >= PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) >= PtNumber(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) >= PtNumber(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) >= PtNumber(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) >= PtNumber(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe false
}
test("testLessThan") {
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) < PtNumber(BaseDataType.UBYTE, 101.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) < PtNumber(BaseDataType.UWORD, 255.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) < PtNumber(BaseDataType.FLOAT, 100.1, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) < PtNumber(BaseDataType.UBYTE, 101.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) < PtNumber(BaseDataType.UWORD, 255.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) < PtNumber(BaseDataType.FLOAT, 100.1, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) <= PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) <= PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) <= PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) <= PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe true
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) < PtNumber(BaseDataType.UBYTE, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) < PtNumber(BaseDataType.UWORD, 254.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) < PtNumber(BaseDataType.FLOAT, 100.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) < PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) < PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) < PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, dummyPos) <= PtNumber(BaseDataType.UBYTE, 99.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, dummyPos) <= PtNumber(BaseDataType.UWORD, 253.0, dummyPos)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, dummyPos) <= PtNumber(BaseDataType.FLOAT, 99.9, dummyPos)) shouldBe false
(PtNumber(BaseDataType.UBYTE, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.UBYTE, 99.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.UWORD, 254.0, Position.DUMMY) <= PtNumber(BaseDataType.UWORD, 253.0, Position.DUMMY)) shouldBe false
(PtNumber(BaseDataType.FLOAT, 100.0, Position.DUMMY) <= PtNumber(BaseDataType.FLOAT, 99.9, Position.DUMMY)) shouldBe false
}
})
+2 -2
View File
@@ -208,8 +208,8 @@ private fun makeSt(): SymbolTable {
val sub12 = StNode("sub2", StNodeType.SUBROUTINE, astSub2)
block1.add(sub11)
block1.add(sub12)
block1.add(StConstant("c1", BaseDataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", BaseDataType.UWORD, 999.0, astConstant2))
block1.add(StConstant("c1", DataType.UWORD, 12345.0, astConstant1))
block1.add(StConstant("blockc", DataType.UWORD, 999.0, astConstant2))
sub11.add(StStaticVariable("v1", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v1))
sub11.add(StStaticVariable("v2", DataType.BYTE, null, null, null, ZeropageWish.DONTCARE, 0u, false, astSub1v2))
sub11.add(StMemVar("v3", DataType.FLOAT, 12345u, null, astSub1v3))
+1 -1
View File
@@ -598,7 +598,7 @@ main {
compileText(C64Target(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain ":9:"
errors.errors[0] shouldContain "no cast"
errors.errors[0] shouldContain "no implicit cast"
errors.errors[1] shouldContain ":10:"
errors.errors[1] shouldContain "out of range"
errors.errors[2] shouldContain ":10:"
+33
View File
@@ -489,4 +489,37 @@ main {
errors.errors[1] shouldContain "string var must be initialized with a string literal"
errors.errors[2] shouldContain "string var must be initialized with a string literal"
}
test("cannot assign strings by-value") {
val src="""
main {
sub start() {
str n1 = "irmen"
str n2 = "de jong 12345"
n1 = n2
n1 = "zsdfzsdf"
n1 = 0
n2 = 9999
void foo(n1)
}
sub foo(str arg) -> str {
arg = "bar"
arg = 0
arg = 9999
return arg
}
}"""
val errors = ErrorReporterForTests()
compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain ":8:9: cannot assign to string"
errors.errors[1] shouldContain ":9:9: cannot assign to string"
errors.errors[2] shouldContain ":10:9: cannot assign to string"
errors.errors[3] shouldContain ":11:9: cannot assign to string"
}
})
+57 -26
View File
@@ -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()
}
})
+5 -5
View File
@@ -271,14 +271,14 @@ class TestProg8Parser: FunSpec( {
val srcText = "bad * { }\n"
val e = shouldThrow<ParseError> { parseModule(SourceCode.Text(srcText)) }
assertPosition(e.position, Regex("^string:[0-9a-f\\-]+$"), 1, 4, 4)
assertPosition(e.position, Regex("^string:[0-9a-f\\-]+$"), 1, 5, 5)
}
test("in ParseError from bad file source code") {
val path = assumeReadableFile(fixturesDir, "ast_file_with_syntax_error.p8")
val e = shouldThrow<ParseError> { parseModule(ImportFileSystem.getFile(path)) }
assertPosition(e.position, SourceCode.relative(path).toString(), 2, 4)
assertPosition(e.position, SourceCode.relative(path).toString(), 2, 5)
}
test("of Module parsed from a string") {
@@ -327,7 +327,7 @@ class TestProg8Parser: FunSpec( {
val mpf = module.position.file
val targetDirective = module.statements.filterIsInstance<Directive>()[0]
assertPositionOf(targetDirective, mpf, 1, 1, 8)
assertPositionOf(targetDirective, mpf, 1, 1, 9)
val mainBlock = module.statements.filterIsInstance<Block>()[0]
assertPositionOf(mainBlock, mpf, 2, 1, 4)
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
@@ -992,7 +992,7 @@ main {
; curly braces without newline
sub start () { foo() derp() other() }
sub foo() { cx16.r0++ }
asmsub derp() { %asm {{ nop rts }} %ir {{ load.b r0,1 return }} }
asmsub derp() { %asm {{ nop rts }} %ir {{ load.b r0,#1 return }} }
; curly braces on next line
sub other()
@@ -1016,7 +1016,7 @@ main {
{
%ir
{{
load.b r0,1
load.b r0,#1
return
}}
}
+115 -7
View File
@@ -126,6 +126,24 @@ txt {
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
}
test("infinite alias loop detected") {
val src="""
main {
sub start() {
alias vv = vv
alias xx = xx.yy
alias zz = mm
alias mm = zz
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "references itself"
errors.errors[1] shouldContain "undefined symbol: xx.yy"
errors.errors[2] shouldContain "references itself"
}
test("wrong alias gives correct error") {
val src="""
main {
@@ -207,20 +225,21 @@ main {
sub start() {
str @shared name = "part1" + "part2"
str @shared rept = "rep"*4
const ubyte times = 3
name = "xx1" + "xx2"
rept = "xyz" * (times+1)
test("xx1" + "xx2")
test("xyz" * 4)
}
sub test(str message) {
}
}"""
val result = compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true)!!
val stmts = result.compilerAst.entrypoint.statements
stmts.size shouldBe 6
stmts.size shouldBe 5
val name1 = stmts[0] as VarDecl
val rept1 = stmts[1] as VarDecl
(name1.value as StringLiteral).value shouldBe "part1part2"
(rept1.value as StringLiteral).value shouldBe "reprepreprep"
val name2strcopy = stmts[3] as IFunctionCall
val rept2strcopy = stmts[4] as IFunctionCall
val name2strcopy = stmts[2] as IFunctionCall
val rept2strcopy = stmts[3] as IFunctionCall
val name2 = (name2strcopy.args.first() as AddressOf).identifier!!
val rept2 = (rept2strcopy.args.first() as AddressOf).identifier!!
(name2.targetVarDecl()!!.value as StringLiteral).value shouldBe "xx1xx2"
@@ -241,6 +260,48 @@ main {
compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.single() shouldContain "cannot use byte value"
}
test("string indexing bounds checks") {
val src="""
main {
sub start() {
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
str name = "hello"
name[5] = '!' ; don't do this in real code...
name[5] = 0
name[6] = 99 ; out of bounds
name[-1] = 99 ; ok
name[-5] = 99 ; ok
cx16.r0L = name[5]
cx16.r1L = name[6] ; out of bounds
cx16.r1L = name[-1] ; ok
cx16.r1L = name[-5] ; ok
array[5] = '!'
array[5] = 0
array[6] = 99 ; out of bounds
array[-1] = 99 ; ok
array[-5] = 99 ; ok
array[-6] = 99 ; ok
cx16.r0L = array[5]
cx16.r1L = array[6] ; out of bounds
cx16.r1L = array[-1] ; ok
cx16.r1L = array[-5] ; ok
cx16.r1L = array[-6] ; ok
}
}"""
val errors = ErrorReporterForTests()
compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=false, errors = errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain ":9:13: index out of bounds"
errors.errors[1] shouldContain ":14:24: index out of bounds"
errors.errors[2] shouldContain ":20:14: index out of bounds"
errors.errors[3] shouldContain ":26:25: index out of bounds"
}
}
context("return value") {
@@ -1091,7 +1152,7 @@ main {
compileText(C128Target(), optimize=false, src, outputDir, writeAssembly=false) shouldNotBe null
}
test("on..goto") {
test("on..goto and on..call") {
val src="""
main {
sub start() {
@@ -1108,6 +1169,23 @@ main {
}
on cx16.r13L+1 goto (thing.func1, thing.func2, thing.func3)
test2()
}
sub test2() {
ubyte @shared thing
on thing call (lblA, lblB, lblC)
on thing goto (lblA, lblB, lblC)
lblA:
cx16.r0++
goto lblDone
lblB:
cx16.r1++
goto lblDone
lblC:
cx16.r2++
lblDone:
}
}
@@ -1192,5 +1270,35 @@ main {
errors.errors.size shouldBe 3
errors.errors[1] shouldContain "12:10: undefined symbol: breakpoint"
}
test("struct names cannot be keywords") {
val src="""
main {
sub start() {
struct on {
bool flag
}
struct step {
bool flag
}
struct inline {
bool flag
}
struct call {
bool flag
}
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), optimize=false, src, outputDir, errors=errors) shouldBe null
errors.errors.size shouldBe 4
errors.errors[0] shouldContain "struct name cannot be a keyword"
errors.errors[1] shouldContain "struct name cannot be a keyword"
errors.errors[2] shouldContain "struct name cannot be a keyword"
errors.errors[3] shouldContain "builtin function cannot be redefined"
}
})
+15 -21
View File
@@ -251,32 +251,26 @@ main {
val src="""
main {
sub start() {
ubyte[10] array
array[-11] = 0
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 1
errors.errors[0] shouldContain "out of bounds"
}
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
str name = "hello"
test("bounds checking on strings invalid cases") {
val src="""
main {
sub start() {
str name = "1234567890"
name[10] = 0
name[-1] = 0
name[-11] = 0
name[-5] = 99 ; ok
name[-6] = 99 ; out of bounds
cx16.r1L = name[-5] ; ok
cx16.r1L = name[-6] ; out of bounds
array[-6] = 99 ; ok
array[-7] = 99 ; out of bounds
cx16.r1L = array[-6] ; ok
cx16.r1L = array[-7] ; out of bounds
}
}"""
val errors = ErrorReporterForTests()
compileText(VMTarget(), false, src, outputDir, writeAssembly = false, errors = errors) shouldBe null
errors.errors.size shouldBe 3
errors.errors[0] shouldContain "out of bounds"
errors.errors[1] shouldContain "out of bounds"
errors.errors[2] shouldContain "out of bounds"
errors.errors.size shouldBe 4
errors.errors[0] shouldContain ":8:9: index out of bounds"
errors.errors[1] shouldContain ":10:20: index out of bounds"
errors.errors[2] shouldContain ":12:9: index out of bounds"
errors.errors[3] shouldContain ":14:20: index out of bounds"
}
test("array and string initializer with multiplication") {
+2 -2
View File
@@ -14,7 +14,7 @@ import prog8.code.source.SourceCode
object ParentSentinel : Node {
override val position = Position("<<sentinel>>", 0, 0, 0)
override val position = Position("~sentinel~", 0, 0, 0)
override var parent: Node = this
override fun linkParents(parent: Node) {}
override fun replaceChildNode(node: Node, replacement: Node) {
@@ -394,7 +394,7 @@ open class Module(final override val statements: MutableList<Statement>,
class GlobalNamespace(val modules: MutableList<Module>): Node, INameScope {
override val name = "<<<global>>>"
override val position = Position("<<<global>>>", 0, 0, 0)
override val position = Position("~global~", 0, 0, 0)
override val statements = mutableListOf<Statement>() // not used
override var parent: Node = ParentSentinel
@@ -113,7 +113,8 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
override fun visitDirective(ctx: DirectiveContext): Directive {
val pos = ctx.toPosition()
val position = Position(pos.file, pos.line, pos.startCol, ctx.directivename().UNICODEDNAME().symbol.stopIndex)
val end = ctx.directivename().UNICODEDNAME().symbol.stopIndex - ctx.directivename().UNICODEDNAME().symbol.startIndex
val position = Position(pos.file, pos.line, pos.startCol, pos.startCol+end+1)
if(ctx.directivenamelist() != null) {
val namelist = ctx.directivenamelist().scoped_identifier().map { it.accept(this) as IdentifierReference }
val identifiers = namelist.map { DirectiveArg(it.nameInSource.joinToString("."), null, it.position) }
@@ -385,6 +386,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())
}
@@ -498,7 +506,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
val (registerorpair, statusregister) = parseParamRegister(pctx.register, pctx.toPosition())
if(statusregister!=null) {
throw SyntaxError("can't use status register as param for normal subroutines", Position(pctx.toPosition().file, pctx.register.line, pctx.register.charPositionInLine, pctx.register.charPositionInLine+1))
throw SyntaxError("can't use status register as param for normal subroutines", Position(pctx.toPosition().file, pctx.register.line, pctx.register.charPositionInLine+1, pctx.register.charPositionInLine+1))
}
return SubroutineParameter(identifiername, datatype, zp, registerorpair, pctx.toPosition())
}
@@ -706,7 +714,8 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
pathString
}
// note: beware of TAB characters in the source text, they count as 1 column...
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + start.stopIndex - start.startIndex)
val endOffset = if(start.startIndex<0 || start.stopIndex<0) 0 else start.stopIndex - start.startIndex
return Position(filename, start.line, start.charPositionInLine+1, start.charPositionInLine + 1 + endOffset)
}
private fun getZpOption(tags: List<String>): ZeropageWish = when {
@@ -747,7 +756,7 @@ class Antlr2KotlinVisitor(val source: SourceCode): AbstractParseTreeVisitor<Node
in RegisterOrPair.names -> registerorpair = RegisterOrPair.valueOf(register)
in Statusflag.names -> statusregister = Statusflag.valueOf(register)
else -> {
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine, registerTok.charPositionInLine+1))
throw SyntaxError("invalid register or status flag", Position(pos.file, registerTok.line, registerTok.charPositionInLine+1, registerTok.charPositionInLine+1))
}
}
}
@@ -946,7 +946,8 @@ class NumericLiteral(val type: BaseDataType, // only numerical types allowed
throw FatalAstException("type cast of weird type $type")
}
}
return ValueAfterCast(false, "no cast available from $type to $targettype number=$number", null)
val implicit = if(implicit) "implicit " else ""
return ValueAfterCast(false, "no ${implicit}cast available from $type to $targettype for value $number", null)
}
fun convertTypeKeepValue(targetDt: BaseDataType): ValueAfterCast {
@@ -1765,6 +1766,8 @@ class PtrDereference(
return resultType(target.datatype)
if(target is StructFieldRef)
return resultType(target.type)
if(target is Alias)
return target.target.inferType(program)
TODO("infertype $chain -> $target")
}
@@ -1845,7 +1848,9 @@ class ArrayIndexedPtrDereference(
InferredTypes.knownFor(fieldDt)
}
} else {
require(indexPosition==this.chain.size-1) {"when indexing primitive pointer type there cannot be a field after it"}
require(indexPosition==this.chain.size-1) {
"when indexing primitive pointer type there cannot be a field after it $position"
}
return if(derefLast)
InferredTypes.knownFor(target.datatype.dereference())
else
+4 -4
View File
@@ -59,10 +59,10 @@ object Prog8Parser {
private class AntlrErrorListener(val src: SourceCode): BaseErrorListener() {
override fun syntaxError(recognizer: Recognizer<*, *>?, offendingSymbol: Any?, line: Int, charPositionInLine: Int, msg: String, e: RecognitionException?) {
if (e == null) {
throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), RuntimeException("parse error"))
throw ParseError(msg, Position(src.origin, line, charPositionInLine+1, charPositionInLine+1), RuntimeException("parse error"))
} else {
if(e.offendingToken==null) {
throw ParseError(msg, Position(src.origin, line, charPositionInLine, charPositionInLine), e)
throw ParseError(msg, Position(src.origin, line, charPositionInLine+1, charPositionInLine+1), e)
} else {
throw ParseError(msg, e.getPosition(src.origin), e)
}
@@ -74,8 +74,8 @@ object Prog8Parser {
val offending = this.offendingToken
val line = offending.line
val beginCol = offending.charPositionInLine
val endCol = beginCol + offending.stopIndex - offending.startIndex
return Position(file, line, beginCol, endCol)
val endOffset = if(offending.startIndex<0 || offending.stopIndex<0 || offending.stopIndex<=offending.startIndex) 0 else offending.stopIndex - offending.startIndex
return Position(file, line, beginCol+1, beginCol+1+endOffset)
}
}
@@ -825,6 +825,7 @@ txt {
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers (A,Y)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
uppercase ()
waitkey () -> ubyte @A
@@ -938,6 +938,7 @@ txt {
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers (A,Y)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
uppercase ()
waitkey () -> ubyte @A
@@ -240,7 +240,7 @@ diskio {
list_filenames (str pattern_ptr, uword filenames_buffer, uword filenames_buf_size) -> ubyte
load (str filenameptr, uword address_override) -> uword
load_raw (str filenameptr, uword startaddress) -> uword
load_size (ubyte startbank, uword startaddress, uword endaddress) -> uword
load_size (ubyte startbank, uword startaddress, uword endaddress) -> long
loadlib (str libnameptr, uword libaddress) -> uword
mkdir (str name)
read4hex () -> uword
@@ -1422,6 +1422,7 @@ txt {
setcc2 (ubyte col, ubyte row, ubyte character, ubyte colors)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A)
setclr (ubyte col @X, ubyte row @Y, ubyte color @A) clobbers (A)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
t256c (bool enable)
uppercase ()
@@ -540,6 +540,7 @@ txt {
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored)
setchr (ubyte col @X, ubyte row @Y, ubyte character @A) clobbers (A,Y)
setclr (ubyte col, ubyte row, ubyte color)
size () clobbers (A) -> ubyte @X, ubyte @Y
spc ()
uppercase ()
waitkey () -> ubyte @A
@@ -559,6 +559,7 @@ txt {
rvs_off ()
rvs_on ()
setchr (ubyte col, ubyte row, ubyte char)
size () -> ubyte, ubyte
spc ()
uppercase ()
width () -> ubyte
+4 -3
View File
@@ -16,11 +16,12 @@ Then you can choose a few ways to get a compiler:
#. download a recent "fat-jar" (called something like "prog8c-all.jar") from `the releases on Github <https://github.com/irmen/prog8/releases>`_
#. run the compiler with "java -jar prog8c.jar" to see how you can use it (use the correct name and version of the jar file you've downloaded).
**Or, install via a Package Manager:**
**Or, install via a Package Manager (takes care of dependencies for you):**
Arch Linux:
Currently, it's available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and compatible systems.
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_.
Currently, it's available on `AUR <https://wiki.archlinux.org/title/Arch_User_Repository>`_ for Arch Linux and derivative systems.
The package is called `"prog8" <https://aur.archlinux.org/packages/prog8>`_. There should be no need to install aything else as
it can automatically pull in the required dependencies.
This package, alongside the compiler itself, also globally installs syntax highlighting for ``vim`` and ``nano``.
In order to run compiler, you can type ``prog8c``. The usage of those commands is exactly the same as with the ``java -jar`` method.
+2 -2
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.
+403 -323
View File
@@ -38,38 +38,6 @@ There's a set of predefined functions in the language. These are fixed and can't
You can use them in expressions and the compiler will evaluate them at compile-time if possible.
Math
^^^^
abs (x)
Returns the absolute value of a number (integer or floating point).
min (x, y)
Returns the smallest of x and y. Supported for integer types only, for floats use ``floats.minf()`` instead.
max (x, y)
Returns the largest of x and y. Supported for integer types only, for floats use ``floats.maxf()`` instead.
clamp (value, minimum, maximum)
Returns the value restricted to the given minimum and maximum.
Supported for integer types only, for floats use ``floats.clampf()`` instead.
sgn (x)
Get the sign of the value (integer or floating point).
The result is a byte: -1, 0 or 1 (negative, zero, positive).
sqrt (w)
Returns the square root of the number.
Supports unsigned integer (result is ubyte) and floating point numbers.
To do the reverse - squaring a number - just write ``x*x``.
divmod (dividend, divisor, quotient, remainder)
Performs division only once and returns both quotient and remainder in a single call, where using '/' and '%' separately
would perform the division operation twice.
All values are ubytes or all are uwords.
The last two arguments must be variables to receive the quotient and remainder results, respectively.
Array operations
^^^^^^^^^^^^^^^^
@@ -81,6 +49,37 @@ len (x)
(use the ``strings.length`` routine if you want to dynamically determine the length by counting to the
first 0-byte)
Math
^^^^
abs (x)
Returns the absolute value of a number (integer or floating point).
clamp (value, minimum, maximum)
Returns the value restricted to the given minimum and maximum.
Supported for integer types only, for floats use ``floats.clampf()`` instead.
divmod (dividend, divisor, quotient, remainder)
Performs division only once and returns both quotient and remainder in a single call, where using '/' and '%' separately
would perform the division operation twice.
All values are ubytes or all are uwords.
The last two arguments must be variables to receive the quotient and remainder results, respectively.
max (x, y)
Returns the largest of x and y. Supported for integer types only, for floats use ``floats.maxf()`` instead.
min (x, y)
Returns the smallest of x and y. Supported for integer types only, for floats use ``floats.minf()`` instead.
sgn (x)
Get the sign of the value (integer or floating point).
The result is a byte: -1, 0 or 1 (negative, zero, positive).
sqrt (x)
Returns the square root of the number.
Accepts unsigned integer (result is ubyte), long (result is uword, but this may not be implemented on all targets), and floating point numbers.
To do the reverse - squaring a number - just write ``x*x``.
Miscellaneous
^^^^^^^^^^^^^
@@ -216,7 +215,8 @@ sizeof (name) ; sizeof (number) ; sizeof(datatype)
The constant number of bytes that the object 'name', the number 'number' or the type 'datatype' occupies in memory.
For instance, for a variable of type uword, the sizeof is 2.
For an 10 element array of floats, it is 50 (on the C64, where a float is 5 bytes).
Note: usually you will be interested in the number of elements in an array, use len() for that.
For a string, it returns the size of the string in memory (which includes the 0-byte terminator at the end)
Note: usually you will be interested in the number of elements in an array, or the number of characters in the string; use len() for that.
memory (name, size, alignment)
Returns the address of the first location of a statically "reserved" block of memory of the given size in bytes,
@@ -279,8 +279,13 @@ Grouped per compilation target.
* `pet32 <_static/symboldumps/skeletons-pet32.txt>`_
* `virtual <_static/symboldumps/skeletons-virtual.txt>`_
Library modules
---------------
bcd
---
^^^
Decimal addition and subtraction routines, so for example $0987 + $1111 = $2098 (rather than the usual hex outcome $1a98)
Utilizes the BCD mode of the CPU (note: not all 6502 variants support this mode).
This mode is useful for example for counting decimal score in a game, to avoid costly conversion to a decimal display string:
@@ -301,7 +306,8 @@ Available routines:
bmx (cx16 only)
----------------
^^^^^^^^^^^^^^^^
Routines to load and save "BMX" files, the CommanderX16 bitmap file format:
`BMX file format specification <https://cx16forum.com/forum/viewtopic.php?t=6945>`_
Only the *uncompressed* bitmaps variant is supported in this library for now.
@@ -315,7 +321,8 @@ There's also the "showbmx" example to look at.
buffers (experimental)
----------------------
^^^^^^^^^^^^^^^^^^^^^^
A small library providing a 8 KB stack, an 8 KB ringbuffer, and a fast 256 bytes ringbuffer.
API is experimental and may change or disappear in a future version.
Stack is a LIFO container, ringbuffers are FIFO containers.
@@ -327,7 +334,8 @@ to see what's in there. Note that the init() routines have that extra bank param
compression (slightly experimental)
-----------------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Routines for data compression and decompression. Currently only the 'ByteRun1' aka 'PackBits' RLE encoding
is available. This is the compression that was also used in Amiga IFF images and in old MacPaint images.
API is slightly experimental and may change in a future version.
@@ -406,7 +414,8 @@ API is slightly experimental and may change in a future version.
conv
----
^^^^
Routines to convert strings to numbers or vice versa.
- numbers to strings, in various formats (binary, hex, decimal)
@@ -418,7 +427,8 @@ to see what's in there.
coroutines
----------
^^^^^^^^^^
Provides a system to make cooperative multitasking programs via coroutines.
A 'coroutine' is a subroutine whose execution you can pause and resume.
This library handles the voodoo for you to switch between such coroutines transparently,
@@ -448,7 +458,8 @@ Here is a minimal example (if the library gets more stable, better docs will be
cx16
----
^^^^
This is available on *all targets*, it is always imported as part of syslib.
On the Commander X16 this module contains a *whole bunch* of things specific to that machine.
It's way too much to include here, you have to study the
@@ -475,7 +486,8 @@ On the other targets, it only contains the definition of the 16 memory-mapped vi
cx16logo
--------
^^^^^^^^
Just a fun module that contains the Commander X16 logo in PETSCII graphics
and allows you to print it anywhere on the screen.
@@ -486,7 +498,8 @@ and allows you to print it anywhere on the screen.
diskio
------
^^^^^^
Provides several routines that deal with disk drive I/O, such as:
- list files on disk, optionally filtering by a simple pattern with ? and *
@@ -535,7 +548,8 @@ to see what's in there. (Note: slight variations for different compiler targets)
emudbg (cx16 only)
-------------------
^^^^^^^^^^^^^^^^^^^
X16Emu Emulator debug routines, for Cx16 only.
Allows you to interface with the emulator's debug routines/registers.
There's stuff like ``is_emulator`` to detect if running in the emulator,
@@ -557,7 +571,7 @@ Information about the exposed debug registers is in the `emulator's documentatio
floats
------
^^^^^^
.. note::
Floating point support is only available on c64, cx16 and virtual targets for now.
@@ -582,6 +596,9 @@ Provides definitions for the ROM/Kernal subroutines and utility routines dealing
``ceil (x)``
Rounds the floating point up to an integer towards positive infinity.
``clampf (value, minimum, maximum)``
returns the value restricted to the given minimum and maximum.
``cos (x)``
Cosine.
@@ -597,6 +614,17 @@ Provides definitions for the ROM/Kernal subroutines and utility routines dealing
``floor (x)``
Rounds the floating point down to an integer towards minus infinity.
``interpolate(v, inputMin, inputMax, outputMin, outputMax)``
Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
``lerp(v0, v1, t)``
Linear interpolation (LERP). Precise method, which guarantees v = v1 when t = 1.
Returns an interpolation between two inputs (v0, v1) for a parameter t in the closed unit interval [0.0, 1.0]
``lerp_fast(v0, v1, t)``
Linear interpolation (LERP). Imprecise (but faster) method, which does not guarantee v = v1 when t = 1
Teturns an interpolation between two inputs (v0, v1) for a parameter t in the closed unit interval [0.0, 1.0]
``ln (x)``
Natural logarithm (base e).
@@ -609,20 +637,24 @@ Provides definitions for the ROM/Kernal subroutines and utility routines dealing
``maxf (x, y)``
returns the largest of x and y.
``clampf (value, minimum, maximum)``
returns the value restricted to the given minimum and maximum.
``parse (stringvalue)``
Parses the string value as floating point number.
Warning: this routine may stop working on the Commander X16 when a new ROM version is released,
because it uses an internal BASIC routine. Then it will require a fix.
``print (x)``
Prints the floating point number x as a string.
There's no leading whitespace (unlike cbm BASIC when printing a floating point number)
``tostr (x)``
Converts the floating point number x to a string (returns address of the string buffer)
There's no leading whitespace.
``rad (x)``
Degrees to radians.
``rnd ()``
returns the next random float between 0.0 and 1.0 from the Pseudo RNG sequence.
``rndseed (seed)``
Sets a new seed for the float pseudo-RNG sequence. Use a negative non-zero number as seed value.
``round (x)``
Rounds the floating point to the closest integer.
@@ -635,31 +667,14 @@ Provides definitions for the ROM/Kernal subroutines and utility routines dealing
``tan (x)``
Tangent.
``rnd ()``
returns the next random float between 0.0 and 1.0 from the Pseudo RNG sequence.
``rndseed (seed)``
Sets a new seed for the float pseudo-RNG sequence. Use a negative non-zero number as seed value.
``parse (stringvalue)``
Parses the string value as floating point number.
Warning: this routine may stop working on the Commander X16 when a new ROM version is released,
because it uses an internal BASIC routine. Then it will require a fix.
``lerp(v0, v1, t)``
Linear interpolation (LERP). Precise method, which guarantees v = v1 when t = 1.
Returns an interpolation between two inputs (v0, v1) for a parameter t in the closed unit interval [0.0, 1.0]
``lerp_fast(v0, v1, t)``
Linear interpolation (LERP). Imprecise (but faster) method, which does not guarantee v = v1 when t = 1
Teturns an interpolation between two inputs (v0, v1) for a parameter t in the closed unit interval [0.0, 1.0]
``interpolate(v, inputMin, inputMax, outputMin, outputMax)``
Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
``tostr (x)``
Converts the floating point number x to a string (returns address of the string buffer)
There's no leading whitespace.
gfx_lores and gfx_hires (cx16 only)
-----------------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Full-screen multicolor bitmap graphics routines, available on the X16 machine only.
- gfx_lores: optimized routines for 320x240 256 color bitmap graphics mode. Compatible with X16 screen mode 128.
@@ -678,7 +693,8 @@ They share the same routines.
graphics
--------
^^^^^^^^
Bitmap graphics routines:
- clearing the screen
@@ -694,18 +710,102 @@ to see what's in there. (Note: slight variations for different compiler targets)
math
----
^^^^
Low-level integer math routines (which you usually don't have to bother with directly, but they are used by the compiler internally).
Pseudo-Random number generators (byte and word).
Various 8-bit integer trig functions that use lookup tables to quickly calculate sine and cosines.
Usually a custom lookup table is the way to go if your application needs these,
but perhaps the provided ones can be of service too.
``log2 (ubyte v)``
Returns the 2-Log of the byte value v.
checksumming
''''''''''''
``log2w (uword v)``
Returns the 2-Log of the word value v.
``crc16 (uword data, uword length) -> uword``
Returns a CRC-16 (XMODEM) checksum over the given data buffer.
Note: on the Commander X16, there is a CRC-16/IBM-3740 routine in the kernal: cx16.memory_crc().
That one is faster, but yields different results.
``crc16_start() / crc16_update(ubyte value) / crc16_end() -> uword``
"streaming" crc16 calculation routines, when the data doesn't fit in a single buffer.
Tracks the crc16 checksum in cx16.r15! If your code uses that, it must save/restore it before calling this routine!
Call the start() routine first, feed it bytes with the update() routine, finalize with calling the end() routine which returns the crc16 value.
Note: after calling the crc16_end() routine you must start over.
``crc32 (uword data, uword length) -> long``
Calculates a CRC-32 (ISO-HDLC/PKZIP) checksum over the given data buffer.
The 32 bits result is returned as a long value. The routine clobbers R0/R1 and R12 through R15.
``crc32_start() / crc32_update(ubyte value) / crc32_end() / crc32_end_result()``
"streaming" crc32 calculation routines, when the data doesn't fit in a single buffer.
Tracks the crc32 checksum in cx16.r14 and cx16.r15! If your code uses these, it must save/restore them before calling this routine!
Call the start() routine first, feed it bytes with the update() routine, finalize with calling the end() routine that returns the result value as a long.
Instead of the normal end() routine you can also call crc32_end_result() which finalizes the calculation,
and actually returns the high and low words of the 32 bits result value as two return word values.
Note: after calling the crc32_end() or crc32_end_result() routine you must start over.
interpolation
'''''''''''''
``lerp(v0, v1, t)``
Linear interpolation routine for unsigned byte values.
Returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
Guarantees v = v1 when t = 255. Also works if v0 > v1.
``lerpw(v0, v1, t)``
Linear interpolation routine for unsigned word values.
Returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
Guarantees v = v1 when t = 65535. Also works if v0 > v1.
Clobbers R15.
``interpolate(v, inputMin, inputMax, outputMin, outputMax)``
Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
All values are unsigned bytes. Clobbers R15
(there is no version for word values because of lack of precision in the fixed point calculation there).
large multiplications
'''''''''''''''''''''
``mul32 (woord w1, word w2) -> long``
Returns the 32 bits signed long result of w1 * w2
``mul16_last_upper () -> uword``
Fetches the upper 16 bits of the previous 16*16 bit multiplication.
To avoid corrupting the result, it is best performed immediately after the multiplication.
Note: It is only for the regular 6502 cpu multiplication routine.
It does not work for the verafx multiplication routines on the Commander X16!
These have a different way to obtain the upper 16 bits of the result: just read cx16.r0.
**NOTE:** the result is only valid if the multiplication was done with uword arguments (or two positive word arguments).
As soon as a single negative word value (or both) was used in the multiplication, these upper 16 bits are not valid!
Suggestion (if you are on the Commander X16): use ``verafx.muls()`` to get a hardware accelerated 32 bit signed multiplication.
miscellaneous
'''''''''''''
``direction (ubyte x1, ubyte y1, ubyte x2, ubyte y2)``
From a pair of positive coordinates, calculate discrete direction between 0 and 23.
This is a heavily optimized routine (small and fast).
``direction_sc (byte x1, byte y1, byte x2, byte y2)``
From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23.
This is a heavily optimized routine (small and fast).
``direction_qd (ubyte quadrant, ubyte xdelta, ubyte ydelta)``
If you already know the quadrant and x/y deltas, calculate discrete direction between 0 and 23.
This is a heavily optimized routine (small and fast).
``diff (ubyte b1, ubyte b2) -> ubyte``
Returns the absolute difference, or distance, between the two byte values.
(This routine is more efficient than doing a compare and a subtract separately, or using abs)
``diffw (uword w1, uword w2) -> uword``
Returns the absolute difference, or distance, between the two word values.
(This routine is more efficient than doing a compare and a subtract separately, or using abs)
random numbers
''''''''''''''
``rnd ()``
Returns next random byte 0-255 from the pseudo-RNG sequence.
@@ -748,6 +848,15 @@ but perhaps the provided ones can be of service too.
Sets a new seed for the pseudo-RNG sequence of the ROM version of the RNG (both rnd and rndw). The seed consists of two words.
Do not use zeros for either of the seed values!
``log2 (ubyte v)``
Returns the 2-Log of the byte value v.
``log2w (uword v)``
Returns the 2-Log of the word value v.
trigonometry
''''''''''''
.. hint::
This is a graph showing the various ranges of values mentioned in the integer sine and cosine
routines that follow below. (Note that the x input value never corresponds to an exact *degree*
@@ -756,6 +865,10 @@ but perhaps the provided ones can be of service too.
.. image:: sinegraph.svg
``atan2 (ubyte x1, ubyte y1, ubyte x2, ubyte y2)``
Fast arctan routine that uses more memory because of large lookup tables.
Calculate the angle, in a 256-degree circle, between two points in the positive quadrant.
``sin8u (x)``
Fast 8-bit ubyte sine.
x = angle 0...2π scaled as 0...255. Result is unsigned, scaled as 0...255
@@ -792,86 +905,12 @@ but perhaps the provided ones can be of service too.
x = of angle 0...2π scaled as 0...179 (so each value increment is a 2° step). Result is signed, scaled as -127...127.
Input values 180...255 lie outside of the valid input interval and will yield a garbage result!
``atan2 (ubyte x1, ubyte y1, ubyte x2, ubyte y2)``
Fast arctan routine that uses more memory because of large lookup tables.
Calculate the angle, in a 256-degree circle, between two points in the positive quadrant.
``direction (ubyte x1, ubyte y1, ubyte x2, ubyte y2)``
From a pair of positive coordinates, calculate discrete direction between 0 and 23.
This is a heavily optimized routine (small and fast).
``direction_sc (byte x1, byte y1, byte x2, byte y2)``
From a pair of signed coordinates around the origin, calculate discrete direction between 0 and 23.
This is a heavily optimized routine (small and fast).
``direction_qd (ubyte quadrant, ubyte xdelta, ubyte ydelta)``
If you already know the quadrant and x/y deltas, calculate discrete direction between 0 and 23.
This is a heavily optimized routine (small and fast).
``diff (ubyte b1, ubyte b2) -> ubyte``
Returns the absolute difference, or distance, between the two byte values.
(This routine is more efficient than doing a compare and a subtract separately, or using abs)
``diffw (uword w1, uword w2) -> uword``
Returns the absolute difference, or distance, between the two word values.
(This routine is more efficient than doing a compare and a subtract separately, or using abs)
``mul32 (woord w1, word w2) -> long``
Returns the 32 bits signed long result of w1 * w2
``mul16_last_upper () -> uword``
Fetches the upper 16 bits of the previous 16*16 bit multiplication.
To avoid corrupting the result, it is best performed immediately after the multiplication.
Note: It is only for the regular 6502 cpu multiplication routine.
It does not work for the verafx multiplication routines on the Commander X16!
These have a different way to obtain the upper 16 bits of the result: just read cx16.r0.
**NOTE:** the result is only valid if the multiplication was done with uword arguments (or two positive word arguments).
As soon as a single negative word value (or both) was used in the multiplication, these upper 16 bits are not valid!
Suggestion (if you are on the Commander X16): use ``verafx.muls()`` to get a hardware accelerated 32 bit signed multiplication.
``crc16 (uword data, uword length) -> uword``
Returns a CRC-16 (XMODEM) checksum over the given data buffer.
Note: on the Commander X16, there is a CRC-16/IBM-3740 routine in the kernal: cx16.memory_crc().
That one is faster, but yields different results.
``crc16_start() / crc16_update(ubyte value) / crc16_end() -> uword``
"streaming" crc16 calculation routines, when the data doesn't fit in a single buffer.
Tracks the crc16 checksum in cx16.r15! If your code uses that, it must save/restore it before calling this routine!
Call the start() routine first, feed it bytes with the update() routine, finalize with calling the end() routine which returns the crc16 value.
Note: after calling the crc16_end() routine you must start over.
``crc32 (uword data, uword length) -> long``
Calculates a CRC-32 (ISO-HDLC/PKZIP) checksum over the given data buffer.
The 32 bits result is returned as a long value. The routine clobbers R0/R1 and R12 through R15.
``crc32_start() / crc32_update(ubyte value) / crc32_end() / crc32_end_result()``
"streaming" crc32 calculation routines, when the data doesn't fit in a single buffer.
Tracks the crc32 checksum in cx16.r14 and cx16.r15! If your code uses these, it must save/restore them before calling this routine!
Call the start() routine first, feed it bytes with the update() routine, finalize with calling the end() routine that returns the result value as a long.
Instead of the normal end() routine you can also call crc32_end_result() which finalizes the calculation,
and actually returns the high and low words of the 32 bits result value as two return word values.
Note: after calling the crc32_end() or crc32_end_result() routine you must start over.
``lerp(v0, v1, t)``
Linear interpolation routine for unsigned byte values.
Returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 255]
Guarantees v = v1 when t = 255. Also works if v0 > v1.
``lerpw(v0, v1, t)``
Linear interpolation routine for unsigned word values.
Returns an interpolation between two inputs (v0, v1) for a parameter t in the interval [0, 65535]
Guarantees v = v1 when t = 65535. Also works if v0 > v1.
Clobbers R15.
``interpolate(v, inputMin, inputMax, outputMin, outputMax)``
Interpolate a value v in interval [inputMin, inputMax] to output interval [outputMin, outputMax]
All values are unsigned bytes. Clobbers R15
(there is no version for word values because of lack of precision in the fixed point calculation there).
There is no ``tan`` function in this library (there is a floating point ``tan`` though in the ``floats`` library).
monogfx (cx16 and virtual)
---------------------------
^^^^^^^^^^^^^^^^^^^^^^^^^^^
Full-screen lores or hires monochrome bitmap graphics routines, available on the X16 machine only.
- two resolutions: lores 320*240 or hires 640*480 bitmap mode
@@ -889,7 +928,8 @@ and the `testmonogfx` example program, to see what's in there.
palette (cx16 only)
--------------------
^^^^^^^^^^^^^^^^^^^^
Available for the Cx16 target. Various routines to set the display color palette.
There are also a few better looking Commodore 64 color palettes available here,
because the Commander X16's default colors for this (the first 16 colors) are too saturated
@@ -903,13 +943,15 @@ to see what's in there.
prog8_lib
---------
^^^^^^^^^
Low-level language support. You should not normally have to bother with this directly.
The compiler needs it for various built-in system routines.
psg (cx16 only)
----------------
^^^^^^^^^^^^^^^^
Available for the Cx16 target.
Contains a simple abstraction for the Vera's PSG (programmable sound generator) to play simple waveforms.
It includes an interrupt routine to handle simple Attack/Release envelopes as well.
@@ -920,7 +962,8 @@ to see what's in there.
sorting (experimental)
----------------------
^^^^^^^^^^^^^^^^^^^^^^
Various sorting routines (gnome sort and shell sort variants) for byte, word and string arrays.
API is experimental and may change or disappear in a future version.
**NOTE:** all word and str arrays have to be @nosplit! Words and pointers need to be consecutive in memory for now.
@@ -930,7 +973,8 @@ to see what's in there. Also check out the `sortingbech` example.
sprites (cx16 only)
--------------------
^^^^^^^^^^^^^^^^^^^^
Available for the Cx16 target. Simple routines to manipulate sprites.
They're not written for high performance, but for simplicity.
That's why they control one sprite at a time. The exception is the ``pos_batch`` routine,
@@ -942,103 +986,12 @@ to see what's in there.
strings
-------
^^^^^^^
Provides string manipulation routines.
``length (str) -> ubyte length``
Number of bytes in the string. This value is determined during runtime and counts upto
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
Don't confuse this with ``len`` and ``sizeof``!
``left (source, length, target)``
Copies the left side of the source string of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
Modifies in-place, doesn't return a value (so can't be used in an expression).
``right (source, length, target)``
Copies the right side of the source string of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
Modifies in-place, doesn't return a value (so can't be used in an expression).
``slice (source, start, length, target)``
Copies a segment from the source string, starting at the given index,
and of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that start and length are within bounds of the strings.
Modifies in-place, doesn't return a value (so can't be used in an expression).
``find (string, char) -> ubyte index, bool found``
Locates the first index of the given character in the string, and a boolean (in Carry flag)
to say if it was found at all. If the character is not found, index 255 (and false) is returned.
You can consider this a safer way of checking if a character occurs
in a string than using an `in` containment check - because this find routine
properly stops at the first 0-byte string terminator it encounters in case the string was modified.
``rfind (string, char) -> ubyte index, bool found``
Like ``find``, but now looking from the *right* of the string instead.
``contains (string, char) -> bool``
Just returns true if the character is in the given string, or false if it's not.
For string literals, you can use a containment check expression instead: ``char in "hello world"``.
``compare (string1, string2) -> ubyte result``
Returns -1, 0 or 1 depending on whether string1 sorts before, equal or after string2.
Note that you can also directly compare strings and string values with each other
using ``==``, ``<`` etcetera (it will use strings.compare for you under water automatically).
This even works when dealing with uword (pointer) variables when comparing them to a string type.
``ncompare (string1, string2, length) -> ubyte result``
Compares two strings up to the number of characters in the length parameter.
Returns -1, 0 or 1 depending on whether string1 sorts before, equal or after string2.
Note that lengths of 0 or 1 evaluate the same. The first character is always compared.
A length larger than either string will function identically to compare.
``copy (from, to) -> ubyte length``
Copy a string to another, overwriting that one. Returns the length of the string that was copied.
Often you don't have to call this explicitly and can just write ``string1 = string2``
but this function is useful if you're dealing with addresses for instance.
``append (string, suffix) -> ubyte length``
Appends the suffix string to the other string (make sure the memory buffer is large enough!)
Returns the length of the combined string.
``lower (string)``
Lowercases the PETSCII-string in place.
``upper (string)``
Uppercases the PETSCII-string in place.
``lowerchar (char)``
Returns lowercased PETSCII character.
``upperchar (char)``
Returns uppercased PETSCII character.
``strip (string)``
Gets rid of whitespace and other non-visible characters at the edges of the string. (destructive)
``rstrip (string)``
Gets rid of whitespace and other non-visible characters at the end of the string. (destructive)
``lstrip (string)``
Gets rid of whitespace and other non-visible characters at the start of the string. (destructive)
``lstripped (string) -> str``
Returns pointer to first non-whitespace and non-visible character at the start of the string (non-destructive lstrip)
``trim (string)``
Gets rid of whitespace characters at the edges of the string. (destructive)
``rtrim (string)``
Gets rid of whitespace characters at the end of the string. (destructive)
``ltrim (string)``
Gets rid of whitespace characters at the start of the string. (destructive)
``ltrimmed (string) -> str``
Returns pointer to first non-whitespace character at the start of the string (non-destructive ltrim)
conversion and classification
'''''''''''''''''''''''''''''
``isdigit (char)``
Returns boolean if the character is a numerical digit 0-9
@@ -1052,17 +1005,38 @@ Provides string manipulation routines.
``isprint (char)``
Returns true if the PETSCII character is a "printable" character (space or any visible symbol)
``startswith (string, prefix) -> bool``
Returns true if string starts with prefix, otherwise false
``lower (string)``
Lowercases the PETSCII-string in place.
``endswith (string, suffix) -> bool``
Returns true if string ends with suffix, otherwise false
``upper (string)``
Uppercases the PETSCII-string in place.
``pattern_match (string, pattern) -> bool`` (not on Virtual target)
Returns true if the string matches the pattern, false if not.
'?' in the pattern matches any one character. '*' in the pattern matches any substring.
An empty pattern matches nothing. If you need everything to match, use a single '*'.
Note: this routine does not work when code is in ROM.
``lowerchar (char)``
Returns lowercased PETSCII character.
``upperchar (char)``
Returns uppercased PETSCII character.
miscellaneous
'''''''''''''
``compare (string1, string2) -> ubyte result``
Returns -1, 0 or 1 depending on whether string1 sorts before, equal or after string2.
Note that you can also directly compare strings and string values with each other
using ``==``, ``<`` etcetera (it will use strings.compare for you under water automatically).
This even works when dealing with uword (pointer) variables when comparing them to a string type.
``length (str) -> ubyte length``
Number of bytes in the string. This value is determined during runtime and counts upto
the first terminating 0 byte in the string, regardless of the size of the string during compilation time.
Don't confuse this with ``len`` and ``sizeof``!
``ncompare (string1, string2, length) -> ubyte result``
Compares two strings up to the number of characters in the length parameter.
Returns -1, 0 or 1 depending on whether string1 sorts before, equal or after string2.
Note that lengths of 0 or 1 evaluate the same. The first character is always compared.
A length larger than either string will function identically to compare.
``hash (string) -> ubyte``
Returns a simple 8 bit hash value for the given string.
@@ -1071,8 +1045,102 @@ Provides string manipulation routines.
On the English word list in /usr/share/dict/words it seems to have a pretty even distribution.
manipulation
''''''''''''
``append (string, suffix) -> ubyte length``
Appends the suffix string to the other string. Make sure the memory buffer is large enough to contain the combined strings.
Returns the length of the combined string.
``copy (from, to) -> ubyte length``
Copy a string to another, overwriting that one. Make sure it was large enough to contain the new string.
Returns the length of the string that was copied.
``left (source, length, target)``
Copies the left side of the source string of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
Modifies in-place, doesn't return a value (so can't be used in an expression).
``lstrip (string)``
Gets rid of whitespace and other non-visible characters at the start of the string. (destructive)
``lstripped (string) -> str``
Returns pointer to first non-whitespace and non-visible character at the start of the string (non-destructive lstrip)
``ltrim (string)``
Gets rid of whitespace characters at the start of the string. (destructive)
``ltrimmed (string) -> str``
Returns pointer to first non-whitespace character at the start of the string (non-destructive ltrim)
``nappend (string, suffix, maxlength) -> ubyte length``
Appends the suffix string to the other string, up to the given maximum length of the combined string.
Returns the length of the combined string.
``ncopy (from, to, maxlength) -> ubyte length``
Copy a string to another, overwriting that one, but limited to the given length.
Returns the length of the string that was copied.
``right (source, length, target)``
Copies the right side of the source string of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that length is smaller or equal to the length of the source string.
Modifies in-place, doesn't return a value (so can't be used in an expression).
``rstrip (string)``
Gets rid of whitespace and other non-visible characters at the end of the string. (destructive)
``rtrim (string)``
Gets rid of whitespace characters at the end of the string. (destructive)
``slice (source, start, length, target)``
Copies a segment from the source string, starting at the given index,
and of the given length to target string.
It is assumed the target string buffer is large enough to contain the result.
Also, you have to make sure yourself that start and length are within bounds of the strings.
Modifies in-place, doesn't return a value (so can't be used in an expression).
``strip (string)``
Gets rid of whitespace and other non-visible characters at the edges of the string. (destructive)
``trim (string)``
Gets rid of whitespace characters at the edges of the string. (destructive)
searching
'''''''''
``contains (string, char) -> bool``
Just returns true if the character is in the given string, or false if it's not.
For string literals, you can use a containment check expression instead: ``char in "hello world"``.
``endswith (string, suffix) -> bool``
Returns true if string ends with suffix, otherwise false
``find (string, char) -> ubyte index, bool found``
Locates the first index of the given character in the string, and a boolean (in Carry flag)
to say if it was found at all. If the character is not found, index 255 (and false) is returned.
You can consider this a safer way of checking if a character occurs
in a string than using an `in` containment check - because this find routine
properly stops at the first 0-byte string terminator it encounters in case the string was modified.
``pattern_match (string, pattern) -> bool`` (not on Virtual target)
Returns true if the string matches the pattern, false if not.
'?' in the pattern matches any one character. '*' in the pattern matches any substring.
An empty pattern matches nothing. If you need everything to match, use a single '*'.
Note: this routine does not work when code is in ROM.
``rfind (string, char) -> ubyte index, bool found``
Like ``find``, but now looking from the *right* of the string instead.
``startswith (string, prefix) -> bool``
Returns true if string starts with prefix, otherwise false
syslib
------
^^^^^^
The "system library" for your target machine. It contains many system-specific definitions such
as ROM/Kernal subroutine definitions, memory location constants, and utility subroutines.
@@ -1085,25 +1153,18 @@ depending on the chosen compilation target. Read the :source:`sys lib source cod
sys (part of syslib)
--------------------
``target``
A constant ubyte value designating the target machine that the program is compiled for.
Notice that this is a compile-time constant value and is not determined on the
system when the program is running.
The following return values are currently defined:
^^^^^^^^^^^^^^^^^^^^
- 7 = Neo6502
- 8 = Atari 8 bits
- 16 = Commander X16
- 25 = Foenix F256 family
- 64 = Commodore 64
- 128 = Commodore 128
- 255 = Virtual machine
miscellaneous
'''''''''''''
``cpu_is_65816()``
Returns true if the CPU in the computer is a 65816, false otherwise (6502 cpu).
Note that Prog8 itself has no support yet for this CPU other than detecting its presence.
``disable_caseswitch()`` and ``enable_caseswitch()``
Disable or enable the ability to switch character set case using a keyboard combination.
``exit (returncode)``
Immediately stops the program and exits it, with the returncode in the A register.
Note: custom interrupt handlers remain active unless manually cleared first!
@@ -1138,31 +1199,6 @@ sys (part of syslib)
Compares two blocks of memory of up to 65535 bytes in size.
Returns -1 (255), 0 or 1, meaning: block 1 sorts before, equal or after block 2.
``read_flags () -> ubyte``
Returns the current value of the CPU status register.
``set_carry ()``
Sets the CPU status register Carry flag.
``clear_carry ()``
Clears the CPU status register Carry flag.
``set_irqd ()``
Sets the CPU status register Interrupt Disable flag.
``clear_irqd ()``
Clears the CPU status register Interrupt Disable flag.
``irqsafe_set_irqd ()``
Sets the CPU status register Interrupt Disable flag, in a way that is safe to be used inside a IRQ handler.
Pair with ``irqsafe_clear_irqd()``.
``irqsafe_clear_irqd ()``
Clears the CPU status register Interrupt Disable flag, in a way that is safe to be used inside a IRQ handler.
Pair with ``irqsafe_set_irqd()``. Inside an IRQ handler this makes sure it doesn't inadvertently
clear the irqd status bit, and it can still be used inside normal code as well (where it *does* clear
the irqd status bit if it was cleared before entering).
``progend ()``
Returns the last address of the program in memory + 1. This means: the memory address directly after all the program code and variables,
including the uninitialized ones ("BSS" variables) and the uninitialized memory blocks reserved by the `memory()` function.
@@ -1173,6 +1209,29 @@ sys (part of syslib)
Returns the first address of the program in memory. This usually is $0801 on the C64 and the X16, for example.
On the assembly level: it returns the address of the symbol "``prog8_program_start``".
``reset_system ()``
Soft-reset the system back to initial power-on BASIC prompt.
(called automatically by Prog8 when the main subroutine returns and the program is not using basicsafe zeropage option)
``save_prog8_internals()`` and ``restore_prog8_internals()``
Normally not used in user code, the compiler utilizes these for the internal interrupt logic.
It stores and restores the values of the internal prog8 variables.
This allows other code to run that might clobber these values temporarily.
``target``
A constant ubyte value designating the target machine that the program is compiled for.
Notice that this is a compile-time constant value and is not determined on the
system when the program is running.
The following return values are currently defined:
- 7 = Neo6502
- 8 = Atari 8 bits
- 16 = Commander X16
- 25 = Foenix F256 family
- 64 = Commodore 64
- 128 = Commodore 128
- 255 = Virtual machine
``wait (uword jiffies)``
wait approximately the given number of jiffies (1/60th seconds)
Note: the regular system irq handler has run for this to work as it depends on the system jiffy clock.
@@ -1190,17 +1249,36 @@ sys (part of syslib)
can be used to avoid screen flicker/tearing when updating screen contents.
note: a more accurate way to do this is by using a raster irq handler instead.
``reset_system ()``
Soft-reset the system back to initial power-on BASIC prompt.
(called automatically by Prog8 when the main subroutine returns and the program is not using basicsafe zeropage option)
``disable_caseswitch()`` and ``enable_caseswitch()``
Disable or enable the ability to switch character set case using a keyboard combination.
processor status flags
''''''''''''''''''''''
``clear_carry ()``
Clears the CPU status register Carry flag.
``save_prog8_internals()`` and ``restore_prog8_internals()``
Normally not used in user code, the compiler utilizes these for the internal interrupt logic.
It stores and restores the values of the internal prog8 variables.
This allows other code to run that might clobber these values temporarily.
``clear_irqd ()``
Clears the CPU status register Interrupt Disable flag.
``irqsafe_set_irqd ()``
Sets the CPU status register Interrupt Disable flag, in a way that is safe to be used inside a IRQ handler.
Pair with ``irqsafe_clear_irqd()``.
``irqsafe_clear_irqd ()``
Clears the CPU status register Interrupt Disable flag, in a way that is safe to be used inside a IRQ handler.
Pair with ``irqsafe_set_irqd()``. Inside an IRQ handler this makes sure it doesn't inadvertently
clear the irqd status bit, and it can still be used inside normal code as well (where it *does* clear
the irqd status bit if it was cleared before entering).
``read_flags () -> ubyte``
Returns the current value of the CPU status register.
``set_carry ()``
Sets the CPU status register Carry flag.
``set_irqd ()``
Sets the CPU status register Interrupt Disable flag.
processor stack
'''''''''''''''
``push (value)``
pushes a byte value on the CPU hardware stack.
@@ -1235,7 +1313,8 @@ sys (part of syslib)
textio (txt.*)
--------------
^^^^^^^^^^^^^^
This will probably be the most used library module. It contains a whole lot of routines
dealing with text-based input and output (to the screen). Such as
@@ -1254,12 +1333,22 @@ to see what's in there. (Note: slight variations for different compiler targets)
verafx (cx16 only)
-------------------
^^^^^^^^^^^^^^^^^^^
Available for the Cx16 target. Routines that use the Vera FX logic to accelerate certain operations.
``available``
Returns true if Vera FX is available, false if not (that would be an older Vera chip)
``clear``
Very quickly clear a piece of vram to a given byte value (it writes 4 bytes at a time).
The routine is around 3 times faster as a regular unrolled loop to clear vram.
``copy``
Very quickly copy a portion of the video memory to somewhere else in vram (4 bytes at a time)
Sometimes this is also called "blitting".
This routine is about 50% faster as a regular byte-by-byte copy.
``muls``
The VeraFX signed word 16*16 to 32 multiplier is accessible via the ``muls`` routine.
It is about 4 to 5 times faster than the default 6502 cpu routine for word multiplication.
@@ -1280,15 +1369,6 @@ Available for the Cx16 target. Routines that use the Vera FX logic to accelerate
Note: there is a block level %option "verafxmuls" that automatically replaces all word multiplications in that block
by calls to verafx, but be careful with it because it may interfere with other Vera operations or IRQs.
``clear``
Very quickly clear a piece of vram to a given byte value (it writes 4 bytes at a time).
The routine is around 3 times faster as a regular unrolled loop to clear vram.
``copy``
Very quickly copy a portion of the video memory to somewhere else in vram (4 bytes at a time)
Sometimes this is also called "blitting".
This routine is about 50% faster as a regular byte-by-byte copy.
``transparency``
Set transparent write mode for VeraFX cached writes and also for normal writes to DATA0/DATA.
If enabled, pixels with value 0 do not modify VRAM when written (so they are "transparent")
+4 -4
View File
@@ -918,6 +918,7 @@ You can also reference identifiers defined elsewhere in your code.
of precision though and gives an error if you may be losing a floating point result.
.. _arithmetic:
Arithmetic and Logical expressions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -955,6 +956,7 @@ and ``(true or false) and false`` is false instead of true.
byte @shared b = 44
w = (b as word)*55
w = b*(55 as word)
w = b * $0037
Operators
@@ -1236,10 +1238,8 @@ Otherwise the compiler will warn you about discarding the result of the call.
Multiple return values
^^^^^^^^^^^^^^^^^^^^^^
Subroutines can return more than one value.
For example, ``asmsub`` routines (implemented in assembly code) or ``extsub`` routines
(referencing an external routine in ROM or elsewhere in RAM) can return multiple values spread
across different registers, and even the CPU's status register flags for boolean values.
Normal subroutines can also return multiple values.
``asmsub`` and ``extsub`` routines return their multiple values spread across different registers,
and can also efficiently use the CPU's status register flags for boolean returnvalues.
You have to "multi assign" all return values of the subroutine call to something:
write the assignment targets as a comma separated list, where the element's order corresponds
to the order of the return values declared in the subroutine's signature.
+18 -87
View File
@@ -1,36 +1,23 @@
TODO
====
- implement the TODO in assignPointerDerefExpression()
- implement inplaceLongAdd/Sub in PointerAssignmentGen to make this compile::
struct Node {
long x
}
^^Node np = 20000
np.x += cx16.r0
STRUCTS and TYPED POINTERS
--------------------------
- implement the remaining TODO's in PointerAssignmentsGen.
- optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- optimize the float copying in assignIndexedPointer() (also word and long?)
- optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment)
- implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
- support @nosplit pointer arrays?
- support pointer to pointer?
- support for typed function pointers? (&routine could be typed by default as well then)
- really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
- (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- implement rest of long comparisons in IfElseAsmGen compareLongValues(): expressions operands that might clobber the R14-R15 registers...
- struct/ptr: implement the remaining TODOs in PointerAssignmentsGen.
- struct/ptr: optimize deref in PointerAssignmentsGen: optimize 'forceTemporary' to only use a temporary when the offset is >0
- struct/ptr: optimize the float copying in assignIndexedPointer() (also word and long?)
- struct/ptr: optimize augmented assignments to indexed pointer targets like sprptr[2]^^.y++ (these are now not performend in-place but as a regular assignment)
- struct/ptr: implement even more struct instance assignments (via memcopy) in CodeDesugarer (see the TODO) (add to documentation as well, paragraph 'Structs')
- struct/ptr: support const pointers (simple and struct types) (make sure to change codegen properly in all cases, change remark about this limitation in docs too)
- struct/ptr: support @nosplit pointer arrays?
- struct/ptr: support pointer to pointer?
- struct/ptr: support for typed function pointers? (&routine could be typed by default as well then)
- struct/ptr: really fixing the pointer dereferencing issues (cursed hybrid beween IdentifierReference, PtrDereferece and PtrIndexedDereference) may require getting rid of scoped identifiers altogether and treat '.' as a "scope or pointer following operator"
- struct/ptr: (later, nasty parser problem:) support chaining pointer dereference on function calls that return a pointer. (type checking now fails on stuff like func().field and func().next.field)
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
- make memory mapped variables support more constant expressions such as: &uword MyHigh = &mylong1+2
- fix the line, cols in Position, sometimes they count from 0 sometimes from 1, should both always be 1-based (is this the reason some source lines end up missing in the IR file?)
- handle Alias in a general way in LiteralsToAutoVarsAndRecombineIdentifiers instead of replacing it scattered over multiple functions
- After long variable type is completed: make all constants long by default (remove type name altogether), reduce to target type implictly if the actual value fits.
This will break some existing programs that depend on value wrap arounds, but gives more intuitive constant number handling.
@@ -38,7 +25,8 @@ Future Things and Ideas
- improve ANTLR grammar with better error handling (as suggested by Qwen AI)
- add documentation for more library modules instead of just linking to the source code
- add an Index to the documentation
- allow memory() to occur in array initializer (maybe needed for 2 dimensional arrays?)
- allow memory() to occur in array initializer (maybe needed for 2 dimensional arrays?) i.e. make it a constant (see github issue #192)
- sizeof(pointer) is now always 2 (an uword), make this a variable in the ICompilationTarget so that it could be 4 at the time we might ad a 32-bits 68000 target for example.
- Two- or even multidimensional arrays and chained indexing, purely as syntactic sugar over regular arrays?
- when a complete block is removed because unused, suppress all info messages about everything in the block being removed
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
@@ -51,6 +39,7 @@ Future Things and Ideas
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Change scoping rules for qualified symbols so that they don't always start from the root but behave like other programming languages (look in local scope first), maybe only when qualified symbol starts with '.' such as: .local.value = 33
- something to reduce the need to use fully qualified names all the time. 'with' ? Or 'using <prefix>'?
- detect circular aliases and print proper error message for them
- Improve register load order in subroutine call args assignments:
in certain situations (need examples!), the "wrong" order of evaluation of function call arguments is done which results
in overwriting registers that already got their value, which requires a lot of stack juggling (especially on plain 6502 cpu!)
@@ -106,66 +95,6 @@ IR/VM
- the split word arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
maybe another representation is needed once actual codegeneration is done from the IR...? Should array operations be encoded in a more high level form in the IR?
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers? maybe not, this requires hundreds of lines to change.. :(
- sometimes source lines end up missing in the output p8ir, for example the first assignment is gone in::
sub start() {
cx16.r0L = cx16.r1 as ubyte
cx16.r0sL = cx16.r1s as byte }
More detailed example: not all source lines are correctly reported in the IR file,
for example the below subroutine only shows the sub() line::
sub two() {
cx16.r0 = peekw(ww + cx16.r0L * 2)
}
and for example the below code omits line 5::
[examples/test.p8: line 4 col 6-8] sub start() {
[examples/test.p8: line 6 col 10-13] cx16.r2 = select2()
[examples/test.p8: line 7 col 10-13] cx16.r3 = select3()
[examples/test.p8: line 8 col 10-13] cx16.r4 = select4()
[examples/test.p8: line 9 col 10-13] cx16.r5 = select5()
.. code-block::
%option enable_floats
main {
sub start() {
cx16.r1 = select1()
cx16.r2 = select2()
cx16.r3 = select3()
cx16.r4 = select4()
cx16.r5 = select5()
}
sub select1() -> uword {
cx16.r0L++
return 2000
}
sub select2() -> str {
cx16.r0L++
return 2000
}
sub select3() -> ^^ubyte {
cx16.r0L++
return 2000
}
sub select4() -> ^^bool {
cx16.r0L++
return 2000
}
sub select5() -> ^^float {
cx16.r0L++
return 2000
}
}
Libraries
@@ -185,7 +114,9 @@ Optimizations
- optimize optimizedBitwiseExpr() for const and variable operands to not assign needlessly to R0-R3.
- optimize inplacemodificationLongWithLiteralval() for more shift values such as 8, 16, 24 etc but take sign bit into account!
- optimize simple cases in funcPeekL and funcPokeL
- Port benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up.
- bind types in the Ast much sooner than the simplifiedAst creation, so that we maybe could get rid of InferredType ?
- longvar = lptr^^ now goes via temporary registers, optimize this to avoid using temps. Also check lptr^^ = lvar.
- Port more benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up. (see benchmark-c/ directory)
- Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappeared. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again?
- in Identifier: use typedarray of strings instead of listOf? Other places?
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
+13 -9
View File
@@ -228,9 +228,10 @@ value datatype
-2147483647 .. 2147483647 long (there is no unsigned long right now)
========================= =================
Numeric expressions usually 'stay within their type' unless a cast is used, see :ref:`arithmetic`.
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::
or you can use the full word hexadecimal notation ``$003c``. This is useful in expressions where you want a calculation
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?
@@ -246,7 +247,7 @@ to be done on word values, and don't want to explicitly have to cast everything
operations should work fine. Notably absent for now are multiplication and division of longs.
There is no unsigned long type at the moment, but you can sometimes simply treat the signed
long value as an unsigned 32 bits value just fine.
Operations on long integers take a lot of instructions on 8 bit cpu's so code that uses them
Operations on long integers take a lot of instructions on 8 bit CPUs so code that uses them
a lot will be much slower than when you restrict yourself to 8 or 16 bit values. Use long values sparingly.
.. danger::
@@ -429,18 +430,21 @@ It can be correctly displayed on the screen only if a iso-8859-15 charset has be
iso:"Käse, Straße"
.. sidebar:: str parameters in subroutines
A subroutine parameter declared as type ``str`` will be changed into a ``^^ubyte`` type (pointer to ubyte) instead
because Prog8 doesn't pass strings by value. This means you *can* assign new values to this parameter variable: you can
set it to a new memory address. You're not actually assigning something to a string variable, because that's not what its type actually is.
You can concatenate two string literals using '+', which can be useful to
split long strings over separate lines. But remember that the length
of the total string still cannot exceed 255 characters.
A string literal can also be repeated a given number of times using '*', where the repeat number must be a constant value.
And a new string value can be assigned to another string, but no bounds check is done!
So be sure the destination string is large enough to contain the new value (it is overwritten in memory)::
str string1 = "first part" + "second part"
str string2 = "hello!" * 10
You cannot assign a new value to a ``str`` variable. If you really want to set a new string value into the variable,
you have to explicitly copy it over the old value with ``strings.copy()`` or ``strings.ncopy()`` for instance.
string1 = string2
string1 = "new value"
There are several escape sequences available to put special characters into your string value:
@@ -412,20 +412,28 @@ asmsub waitkey() -> ubyte @A {
asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
; TODO
%asm {{
lda #0
lda #DEFAULT_WIDTH
rts
}}
}
asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
; TODO
%asm {{
lda #0
lda #DEFAULT_HEIGHT
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
ldx #DEFAULT_WIDTH
ldy #DEFAULT_HEIGHT
rts
}}
}
}
+12 -2
View File
@@ -417,7 +417,7 @@ asmsub plot (ubyte col @ Y, ubyte row @ X) {
asmsub width() clobbers(X,Y) -> ubyte @A {
; -- returns the text screen width (number of columns)
%asm {{
lda DEFAULT_WIDTH
lda #DEFAULT_WIDTH
rts
}}
}
@@ -425,11 +425,21 @@ asmsub width() clobbers(X,Y) -> ubyte @A {
asmsub height() clobbers(X, Y) -> ubyte @A {
; -- returns the text screen height (number of rows)
%asm {{
lda DEFAULT_HEIGHT
lda #DEFAULT_HEIGHT
rts
}}
}
asmsub size() clobbers(A) -> ubyte @X, ubyte @Y {
; -- returns the text screen width in X and height in Y (number of columns and rows)
%asm {{
ldx #DEFAULT_HEIGHT
ldy #DEFAULT_HEIGHT
rts
}}
}
; TODO: jmp to cbm.CHRIN?
asmsub waitkey() -> ubyte @A {
%asm {{
+38 -5
View File
@@ -3,11 +3,10 @@
%import math
%import monogfx
%import verafx
%import floats
; TODO add FPS counter
; TODO fix the camera normal calculation for the hidden surface removal
; TODO add all other Elite's ships, show their name, advance to next ship on keypress
; TODO embed pre calculated surface normals???
main {
sub start() {
@@ -83,7 +82,7 @@ main {
ubyte @zp pointIdx = 0
ubyte faceNumber
for faceNumber in shipdata.totalNumberOfFaces -1 downto 0 {
if matrix_math.facing_away(pointIdx) {
if matrix_math.facing_away_fast_but_imprecise(pointIdx) { ;; can also use matrix_math.facing_away_slow_but_precise(pointIdx)
; don't draw this face, fast-forward over the edges and points
edgeIdx += 3 ; every face hast at least 3 edges
while shipdata.facesEdges[edgeIdx]!=255 {
@@ -180,14 +179,15 @@ matrix_math {
}
}
sub facing_away(ubyte edgePointsIdx) -> bool {
sub facing_away_fast_but_imprecise(ubyte edgePointsIdx) -> bool {
; simplistic visibility determination by checking the Z component of the surface normal
; TODO: actually take the line of sight vector into account
; this only compares the surface normal to the screen space vector which doesn't yield the proper perspective correct result, but is fast
ubyte p1 = shipdata.facesPoints[edgePointsIdx]
edgePointsIdx++
ubyte p2 = shipdata.facesPoints[edgePointsIdx]
edgePointsIdx++
ubyte p3 = shipdata.facesPoints[edgePointsIdx]
word p1x = rotatedx[p1] / 128
word p1y = rotatedy[p1] / 128
word p2x = rotatedx[p2] / 128
@@ -196,6 +196,39 @@ matrix_math {
word p3y = rotatedy[p3] / 128
return (p2x-p3x)*(p1y-p3y) - (p2y-p3y)*(p1x-p3x) > 0
}
sub facing_away_slow_but_precise(ubyte edgePointsIdx) -> bool {
; determine visibility by calculating the dot product of surface normal and view vector
ubyte p1 = shipdata.facesPoints[edgePointsIdx]
edgePointsIdx++
ubyte p2 = shipdata.facesPoints[edgePointsIdx]
edgePointsIdx++
ubyte p3 = shipdata.facesPoints[edgePointsIdx]
; Calculate two edge vectors of the triangle (scaled by 2)
word v1x = (rotatedx[p2] - rotatedx[p1])/128
word v1y = (rotatedy[p2] - rotatedy[p1])/128
word v1z = (rotatedz[p2] - rotatedz[p1])/128
word v2x = (rotatedx[p3] - rotatedx[p1])/128
word v2y = (rotatedy[p3] - rotatedy[p1])/128
word v2z = (rotatedz[p3] - rotatedz[p1])/128
; Calculate surface normal using cross product: N = V1 x V2 (scaled by 4)
; Note: because of lack of precision in the 16 bit word math, we need to use floating point math here.... :-(
; Elite had a more optimized version of this algorithm that still used fixed point integer math only...
float normalx = (v1y * v2z - v1z * v2y) as float
float normaly = (v1z * v2x - v1x * v2z) as float
float normalz = (v1x * v2y - v1y * v2x) as float
; Calculate view vector from camera (0,0,-170) to point p1 (scaled by 4)
float viewx = rotatedx[p1]/(256/4) - 0 as float ; from camera x to point x
float viewy = rotatedy[p1]/(256/4) - 0 as float ; from camera y to point y
float viewz = rotatedz[p1]/(256/4) - (-170*4) as float ; from camera z to point z
; Calculate dot product of normal and view vector
; If dot product is negative, the face is pointing away from the camera
return normalx * viewx + normaly * viewy + normalz * viewz < 0
}
}
shipdata {
+20 -20
View File
@@ -12,26 +12,26 @@ main{
}
^^Country[] countries = [
^^Country:["Indonesia", 285.72, 1904],
^^Country:["Congo", 112.83, 2344],
^^Country:["Vietnam", 101.60, 331],
^^Country:["United States", 347.28, 9372],
^^Country:["Iran", 92.42, 1648],
^^Country:["Turkey", 87.69, 783],
^^Country:["Brazil", 212.81, 8515],
^^Country:["Bangladesh", 175.69, 147],
^^Country:["Germany", 84.08, 357],
^^Country:["Japan", 123.10, 377],
^^Country:["India", 1463.87, 3287],
^^Country:["China", 1416.10, 9596],
^^Country:["Philippines", 116.79, 300],
^^Country:["Russia", 143.99, 17098],
^^Country:["Pakistan", 255.22, 881],
^^Country:["Nigeria", 237.53, 923],
^^Country:["Ethiopia", 135.47, 1104],
^^Country:["Mexico", 131.95, 1964],
^^Country:["Thailand", 71.62, 513],
^^Country:["Egypt", 118.37, 1002],
["Indonesia", 285.72, 1904],
["Congo", 112.83, 2344],
["Vietnam", 101.60, 331],
["United States", 347.28, 9372],
["Iran", 92.42, 1648],
["Turkey", 87.69, 783],
["Brazil", 212.81, 8515],
["Bangladesh", 175.69, 147],
["Germany", 84.08, 357],
["Japan", 123.10, 377],
["India", 1463.87, 3287],
["China", 1416.10, 9596],
["Philippines", 116.79, 300],
["Russia", 143.99, 17098],
["Pakistan", 255.22, 881],
["Nigeria", 237.53, 923],
["Ethiopia", 135.47, 1104],
["Mexico", 131.95, 1964],
["Thailand", 71.62, 513],
["Egypt", 118.37, 1002],
]
sub start() {
+23 -31
View File
@@ -1,41 +1,33 @@
%import textio
%import math
%zeropage basicsafe
%option no_sysinit
main {
sub start() {
long @shared lv, lv2
ubyte[] array = ['h', 'e', 'l', 'l', 'o', 0]
str name = "hello"
lv = $11111111
lv2 = $55555555
name[5] = '!' ; don't do this in real code...
name[5] = 0
name[6] = 99 ; out of bounds
name[-1] = 99 ; ok
name[-5] = 99 ; ok
lv = lv | lv2 ^ 999999
cx16.r0L = name[5]
cx16.r1L = name[6] ; out of bounds
cx16.r1L = name[-1] ; ok
cx16.r1L = name[-5] ; ok
txt.print_ulhex(lv, true) ; $555b177b
array[5] = '!'
array[5] = 0
array[6] = 99 ; out of bounds
array[-1] = 99 ; ok
array[-5] = 99 ; ok
array[-6] = 99 ; ok
; long @shared lv1 = $12345678
;
; txt.print_ubhex(msb(lv1), true)
; txt.spc()
; txt.print_ubhex(lsb(lv1), true)
; txt.spc()
; txt.print_ubhex(lv1 as ubyte, true)
; txt.nl()
; txt.print_uwhex(msw(lv1), true)
; txt.spc()
; txt.print_uwhex(lsw(lv1), true)
; txt.nl()
; ; TODO support long+1 / -1 expressions....
; cx16.r4 = msw(lv1-1)
; cx16.r5 = lsw(lv1-1)
; txt.print_uwhex(cx16.r4, true)
; txt.print_uwhex(cx16.r5, true)
; txt.nl()
;
; txt.print_ubhex((lv1-1) as ubyte, true)
; txt.nl()
cx16.r0L = array[5]
cx16.r1L = array[6] ; out of bounds
cx16.r1L = array[-1] ; ok
cx16.r1L = array[-5] ; ok
cx16.r1L = array[-6] ; ok
}
}
+5 -5
View File
@@ -421,7 +421,7 @@ galaxy {
repeat system {
generate_next_planet()
}
planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), planet.name)
init_market_for_planet()
}
@@ -441,7 +441,7 @@ galaxy {
ubyte pi
for pi in 0 to 255 {
generate_next_planet()
planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), planet.name)
if util.prefix_matches(nameptr, planet.name) {
ubyte distance = planet.distance(x, y)
if distance < current_distance {
@@ -479,7 +479,7 @@ galaxy {
else
txt.chrout('-')
txt.spc()
planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), planet.name)
planet.display(true, distance)
}
pn++
@@ -495,7 +495,7 @@ galaxy {
str current_name = " " ; 8 max
ubyte pn = 0
current_name = planet.name
void strings.copy(planet.name, current_name)
init(number)
txt.clear_screen()
txt.print("Galaxy #")
@@ -516,7 +516,7 @@ galaxy {
generate_next_planet()
ubyte distance = planet.distance(px, py)
if distance <= max_distance {
planet.name = make_current_planet_name()
void strings.copy(make_current_planet_name(), planet.name)
planet.name[0] = strings.upperchar(planet.name[0])
uword tx = planet.x
uword ty = planet.y
+1 -1
View File
@@ -4,4 +4,4 @@ org.gradle.parallel=true
org.gradle.daemon=true
org.gradle.configuration-cache=false
kotlin.code.style=official
version=12.0-BETA5
version=12.0-BETA6
@@ -177,7 +177,8 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
xml.writeStartElement("P8SRC")
val sourceTxt = StringBuilder("\n")
code.sourceLinesPositions
.filter{ pos -> pos.line > 0 }
.asSequence()
.filter { it !== Position.DUMMY }
.sortedBy { it.line }
.forEach { pos ->
val line = ImportFileSystem.retrieveSourceLine(pos)
@@ -270,6 +271,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
val value: String = when {
dt.isBool -> constant.value.toInt().toString()
dt.isFloat -> constant.value.toString()
dt.isPointer -> TODO("constant pointer $constant")
dt.isInteger -> constant.value.toInt().toHex()
else -> throw InternalCompilerException("weird dt")
}
@@ -167,7 +167,7 @@ modr reg1, reg2 - remainder (modulo) of unsigned div
mod reg1, value - remainder (modulo) of unsigned division reg1 %= value note: division by zero yields max signed int $ff/$ffff
divmodr reg1, reg2 - unsigned division reg1/reg2, storing division and remainder on value stack (so need to be POPped off)
divmod reg1, value - unsigned division reg1/value, storing division and remainder on value stack (so need to be POPped off)
sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
sqrt reg1, reg2 - reg1 is the square root of reg2 (reg2 can be l.1, .w or .b, result type in reg1 is .w or .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
square reg1, reg2 - reg1 is the square of reg2 (reg2 can be .w or .b, result type in reg1 is always .b) you can also use it with floating point types, fpreg1 and fpreg2 (result is also .f)
sgn reg1, reg2 - reg1.b is the sign of reg2 (or fpreg1, if sgn.f) (0.b, 1.b or -1.b)
cmp reg1, reg2 - set processor status bits C, N, Z according to comparison of reg1 with reg2. (semantics taken from 6502/68000 CMP instruction)
@@ -1102,6 +1102,8 @@ data class IRInstruction(
if(type==IRDataType.LONG) {
if(opcode==Opcode.SGN)
return IRDataType.BYTE
if(opcode==Opcode.SQRT)
return IRDataType.WORD
}
if(opcode==Opcode.JUMPI || opcode==Opcode.CALLI || opcode==Opcode.STOREZI || opcode==Opcode.LSIGW || opcode==Opcode.MSIGW)
return IRDataType.WORD
@@ -572,12 +572,12 @@ class IRCodeChunk(label: String?, next: IRCodeChunkBase?): IRCodeChunkBase(label
}
fun appendSrcPosition(position: Position) {
if(sourceLinesPositions.lastOrNull()!=position)
if(!sourceLinesPositions.contains(position))
sourceLinesPositions.add(position)
}
fun appendSrcPositions(positions: Collection<Position>) {
positions.forEach { appendSrcPosition(it) }
positions.asSequence().filter { it!==Position.DUMMY }.forEach { appendSrcPosition(it) }
}
val sourceLinesPositions = mutableListOf<Position>()
+1 -1
View File
@@ -81,7 +81,7 @@ ubyte main.thing=42
<INITGLOBALS>
<CHUNK><REGS>dummy</REGS><CODE>
load.b r1,42
load.b r1,#42
</CODE></CHUNK>
</INITGLOBALS>
+3 -2
View File
@@ -24,6 +24,7 @@ BLOCK_COMMENT : '/*' ( BLOCK_COMMENT | ~'*' | '*' ~'/' )*? '*/' -> skip ;
WS : [ \t] -> skip ;
// WS2 : '\\' EOL -> skip;
VOID: 'void';
STRUCT: 'struct';
ON: 'on';
GOTO: 'goto';
CALL: 'call';
@@ -96,6 +97,7 @@ block_statement:
statement :
directive
| ongoto
| variabledeclaration
| structdeclaration
| assignment
@@ -117,7 +119,6 @@ statement :
| breakstmt
| continuestmt
| labeldef
| ongoto
| defer
| alias
;
@@ -132,7 +133,7 @@ variabledeclaration :
structdeclaration:
'struct' identifier '{' EOL? (structfielddecl | EOL)+ '}'
STRUCT identifier '{' EOL? (structfielddecl | EOL)+ '}'
;
structfielddecl: datatype identifierlist;
+1 -1
View File
@@ -267,7 +267,7 @@ class StStaticVariable(name: String,
}
class StConstant(name: String, val dt: BaseDataType, val value: Double, astNode: PtNode?) :
class StConstant(name: String, val dt: DataType, val value: Double, astNode: PtNode?) :
StNode(name, StNodeType.CONSTANT, astNode)
+1 -1
View File
@@ -48,7 +48,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
}
is PtConstant -> {
require(node.type.isNumericOrBool)
StConstant(node.name, node.type.base, node.value, node)
StConstant(node.name, node.type, node.value, node)
}
is PtLabel -> {
StNode(node.name, StNodeType.LABEL, node)
+2 -2
View File
@@ -510,10 +510,10 @@ object SysCalls {
}
}
Syscall.STRINGCOPY -> {
val (sourceA, targetA) = getArgValues(callspec.arguments, vm)
val (sourceA, targetA, maxlength) = getArgValues(callspec.arguments, vm)
val source = (sourceA as UShort).toInt()
val target = (targetA as UShort).toInt()
val string = vm.memory.getString(source)
val string = vm.memory.getString(source).take((maxlength as UByte).toInt())
vm.memory.setString(target, string, true)
returnValue(callspec.returns.single(), string.length, vm)
}
+15 -7
View File
@@ -583,7 +583,7 @@ class VirtualMachine(irProgram: IRProgram) {
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg2!!)
val value = memory.getSL(pointer.toInt())
registers.setSL(i.reg1!!, value)
statusbitsNZ(value.toInt(), i.type!!)
statusbitsNZ(value, i.type!!)
}
IRDataType.FLOAT -> {
val pointer = memory.getUW(i.address!!) + registers.getUB(i.reg1!!)
@@ -1444,7 +1444,12 @@ class VirtualMachine(irProgram: IRProgram) {
when(i.type!!) {
IRDataType.BYTE -> registers.setUB(i.reg1!!, sqrt(registers.getUB(i.reg2!!).toDouble()).toInt().toUByte())
IRDataType.WORD -> registers.setUB(i.reg1!!, sqrt(registers.getUW(i.reg2!!).toDouble()).toInt().toUByte())
IRDataType.LONG -> TODO("long sqrt")
IRDataType.LONG -> {
val value = registers.getSL(i.reg2!!)
if(value<0)
throw IllegalArgumentException("sqrt of negative long $value reg=${i.reg2}")
registers.setSL(i.reg1!!, sqrt(value.toDouble()).toInt())
}
IRDataType.FLOAT -> registers.setFloat(i.fpReg1!!, sqrt(registers.getFloat(i.fpReg2!!)))
}
nextPc()
@@ -2526,7 +2531,7 @@ class VirtualMachine(irProgram: IRProgram) {
newStatusCarry = (orig and 0x80000000u) != 0u
val rotated: UInt = (if (useCarry) {
val carry = if (statusCarry) 1u else 0u
(orig.toUInt().rotateLeft(1) or carry)
(orig.rotateLeft(1) or carry)
} else
orig.rotateLeft(1))
registers.setSL(i.reg1!!, rotated.toInt())
@@ -2648,7 +2653,7 @@ class VirtualMachine(irProgram: IRProgram) {
val lsb = registers.getUB(i.reg3!!)
val value = ((msb.toInt() shl 8) or lsb.toInt())
registers.setUW(i.reg1!!, value.toUShort())
statusbitsNZ(value.toInt(), i.type!!)
statusbitsNZ(value, i.type!!)
}
IRDataType.WORD -> {
val msw = registers.getUW(i.reg2!!)
@@ -2959,15 +2964,18 @@ class VirtualMachine(irProgram: IRProgram) {
}
private fun multiplyAnyLongSigned(reg1: Int, reg2: Int) {
TODO("multiplyAnyLongSigned - multiplication and division of long numbers not yet supported, use floats or words")
val result = registers.getSL(reg1) * registers.getSL(reg2)
registers.setSL(reg1, result)
}
private fun multiplyConstLongSigned(reg1: Int, value: Int) {
TODO("multiplyConstLongSigned - multiplication and division of long numbers not yet supported, use floats or words")
val result = registers.getSL(reg1) * value
registers.setSL(reg1, result)
}
private fun multiplyAnyLongSignedInplace(reg1: Int, address: Int) {
TODO("multiplyAnyLongSignedInplace - multiplication and division of long numbers not yet supported, use floats or words")
val result = memory.getSL(address) * registers.getSL(reg1)
memory.setSL(address, result)
}
private fun divModLongSigned(operator: String, reg1: Int, reg2: Int) {