mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79039b66d4 | ||
|
|
833e463525 | ||
|
|
6611e4e092 | ||
|
|
1f31cb18e4 | ||
|
|
bc8ba252a5 | ||
|
|
c353dd40bf | ||
|
|
928ef6bbaa | ||
|
|
45dde856c6 | ||
|
|
e1ccef4e89 | ||
|
|
4d3f0ec223 | ||
|
|
b73c958c4a | ||
|
|
2eac457e1c | ||
|
|
815ef7e654 | ||
|
|
0cd8c4f87e | ||
|
|
b02a8ed954 | ||
|
|
b1e07f3fdb | ||
|
|
fc8727f81e | ||
|
|
ec728bad52 | ||
|
|
a5e827e40f | ||
|
|
34061c5a63 | ||
|
|
9c2bcab4a5 | ||
|
|
2f07f41664 | ||
|
|
7027304597 | ||
|
|
ee75333891 | ||
|
|
6a70fb0480 | ||
|
|
ebc738b132 | ||
|
|
e5939be0bd | ||
|
|
0c0affd1bf | ||
|
|
c7158fd968 | ||
|
|
de2f3f0d0d | ||
|
|
c0286e3349 |
No files matched your search
Generated
+10
-10
@@ -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>
|
||||
@@ -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 |
@@ -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
@@ -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)),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -550,7 +550,7 @@ done:
|
||||
return list_filename
|
||||
|
||||
io_error:
|
||||
list_filename = "io error"
|
||||
void strings.copy("io error", list_filename)
|
||||
goto done
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
})
|
||||
@@ -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
|
||||
}
|
||||
|
||||
})
|
||||
@@ -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))
|
||||
|
||||
@@ -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:"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
})
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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")
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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 {{
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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>()
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user