mirror of
https://github.com/irmen/prog8.git
synced 2026-10-22 05:02:58 +00:00
Compare commits
55
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9504711fc7 | ||
|
|
590feda903 | ||
|
|
6e7e2922bf | ||
|
|
01df1f0083 | ||
|
|
397299bd1d | ||
|
|
c275aacd38 | ||
|
|
6a6e18773e | ||
|
|
99e037489b | ||
|
|
fb5290e17b | ||
|
|
818774ab84 | ||
|
|
d667312acc | ||
|
|
64d0cd87a8 | ||
|
|
2fffbdde89 | ||
|
|
dc8ea31c49 | ||
|
|
f4ead66e91 | ||
|
|
314e7f5691 | ||
|
|
da31465b7f | ||
|
|
397a907088 | ||
|
|
50c6962e1f | ||
|
|
b7d1fb1342 | ||
|
|
afb458a7da | ||
|
|
da3c7f267f | ||
|
|
b7ba7c50b1 | ||
|
|
bdbfe7048b | ||
|
|
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("""
|
||||
@@ -1988,9 +2003,9 @@ $repeatLabel""")
|
||||
lda #<$leftName
|
||||
ldy #>$leftName""")
|
||||
if(lessOrEquals)
|
||||
out("jsr floats.vars_lesseq_f")
|
||||
out(" jsr floats.vars_lesseq_f")
|
||||
else
|
||||
out("jsr floats.vars_less_f")
|
||||
out(" jsr floats.vars_less_f")
|
||||
}
|
||||
fun lessf(expr: PtExpression, rightName: String) {
|
||||
assignExpressionToRegister(expr, RegisterOrPair.FAC1, true)
|
||||
@@ -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)
|
||||
@@ -1933,7 +1938,7 @@ _jump jmp (${target.asmLabel})
|
||||
if(notEquals)
|
||||
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
|
||||
else
|
||||
translateAYNotEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
|
||||
translateAYEquals("P8ZP_SCRATCH_W1", "P8ZP_SCRATCH_W1+1")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,14 @@ $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 -> {
|
||||
asmgen.out("""
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
lda #<$targetAsmVarName
|
||||
ldy #>$targetAsmVarName
|
||||
jsr floats.cast_as_long""")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
}
|
||||
@@ -2988,6 +3094,15 @@ $endLabel""")
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
} else if(targetDt.isWord || targetDt.isPointer) {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||
} else if(targetDt.isFloat) {
|
||||
asmgen.out("""
|
||||
lda #<$targetAsmVarName
|
||||
ldy #>$targetAsmVarName
|
||||
sta cx16.r0L
|
||||
sty cx16.r0H
|
||||
lda #<$sourceAsmVarName
|
||||
ldy #>$sourceAsmVarName
|
||||
jsr floats.cast_from_long""")
|
||||
} else
|
||||
throw AssemblyError("weird type")
|
||||
}
|
||||
@@ -3026,6 +3141,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 +3189,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 +3225,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 +3264,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 +3470,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 +3583,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 +3766,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 +3778,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 +4089,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 +4120,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 +4618,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 +4633,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 +4646,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 +4661,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 +4672,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 */ }
|
||||
}
|
||||
}
|
||||
@@ -5223,7 +5342,7 @@ $endLabel""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), ignoreDatatype, scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceByteNegate(PtrTarget(target), scope)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -5284,7 +5403,7 @@ $endLabel""")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> throw AssemblyError("memory is ubyte, can't negate that")
|
||||
TargetStorageKind.ARRAY -> assignPrefixedExpressionToArrayElt(makePrefixedExprFromArrayExprAssign("-", assign), scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), ignoreDatatype, scope)
|
||||
TargetStorageKind.POINTER -> pointergen.inplaceWordNegate(PtrTarget(target), scope)
|
||||
TargetStorageKind.VOID -> { /* do nothing */ }
|
||||
}
|
||||
}
|
||||
@@ -5309,7 +5428,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 -> pointergen.inplaceLongNegate(PtrTarget(target), scope)
|
||||
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
|
||||
|
||||
|
||||
@@ -116,14 +117,18 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
TODO("inplace word invert pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceByteNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
|
||||
internal fun inplaceByteNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace byte negate to pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceWordNegate(target: PtrTarget, ignoreDatatype: Boolean, scope: IPtSubroutine?) {
|
||||
internal fun inplaceWordNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace word negate pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceLongNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace long negate pointer deref ${target.position}")
|
||||
}
|
||||
|
||||
internal fun inplaceFloatNegate(target: PtrTarget, scope: IPtSubroutine?) {
|
||||
TODO("inplace float negate pointer deref ${target.position}")
|
||||
}
|
||||
@@ -248,8 +253,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 +305,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 +466,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 +996,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}")
|
||||
}
|
||||
}
|
||||
@@ -1159,8 +1329,8 @@ internal class PointerAssignmentsGen(private val asmgen: AsmGen6502Internal, pri
|
||||
|
||||
fun divide(signed: Boolean) {
|
||||
// on entry here: number placed in P8ZP_SCRATCH_PTR, divisor placed in AY
|
||||
if(signed) asmgen.out("jsr prog8_math.divmod_w_asm")
|
||||
else asmgen.out("jsr prog8_math.divmod_uw_asm")
|
||||
if(signed) asmgen.out(" jsr prog8_math.divmod_w_asm")
|
||||
else asmgen.out(" jsr prog8_math.divmod_uw_asm")
|
||||
asmgen.out("""
|
||||
tax
|
||||
tya
|
||||
@@ -1621,7 +1791,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 +2075,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 +2110,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
|
||||
|
||||
@@ -819,6 +819,10 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.EXTS, type = IRDataType.WORD, reg1 = actualResultReg2, reg2=tr.resultReg), null)
|
||||
}
|
||||
BaseDataType.FLOAT -> {
|
||||
actualResultReg2 = codeGen.registers.next(IRDataType.LONG)
|
||||
addInstr(result, IRInstruction(Opcode.FTOSL, IRDataType.FLOAT, reg1=actualResultReg2, fpReg1 = tr.resultFpReg), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast $valueDt to long ${cast.position}")
|
||||
}
|
||||
}
|
||||
@@ -837,6 +841,9 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
BaseDataType.WORD -> {
|
||||
addInstr(result, IRInstruction(Opcode.FFROMSW, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
||||
}
|
||||
BaseDataType.LONG -> {
|
||||
addInstr(result, IRInstruction(Opcode.FFROMSL, IRDataType.FLOAT, reg1=tr.resultReg, fpReg1 = actualResultFpReg2), null)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type ${cast.position}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -> {
|
||||
|
||||
@@ -440,6 +440,54 @@ jump p8_label_gen_2
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
fun optimizeImmediateLoadAssociative(replacementOpcode: Opcode) {
|
||||
|
||||
fun getImmediateLoad(reg: Int): Pair<Int, Int>? {
|
||||
// look if the given register gets an immediate value 1 or 2 istructions back
|
||||
// returns (index of load instruction, immediate value) or null.
|
||||
if(idx>=1) {
|
||||
val previous = indexedInstructions[idx-1].value
|
||||
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
|
||||
return idx-1 to previous.immediate!!
|
||||
}
|
||||
if(idx>=2) {
|
||||
val previous = indexedInstructions[idx-2].value
|
||||
if(previous.opcode==Opcode.LOAD && previous.reg1==reg)
|
||||
return idx-2 to previous.immediate!!
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
if(ins.reg1!=null) {
|
||||
val immediate1 = getImmediateLoad(ins.reg1!!)
|
||||
if(immediate1!=null) {
|
||||
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg2, immediate = immediate1.second)
|
||||
chunk.instructions.removeAt(immediate1.first)
|
||||
changed=true
|
||||
} else {
|
||||
val immediate2 = getImmediateLoad(ins.reg2!!)
|
||||
if (immediate2 != null) {
|
||||
chunk.instructions[idx] = IRInstruction(replacementOpcode, ins.type, reg1 = ins.reg1, immediate = immediate2.second)
|
||||
chunk.instructions.removeAt(immediate2.first)
|
||||
changed=true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try to use immediate arithmetic instruction if possible
|
||||
when(ins.opcode) {
|
||||
Opcode.ADDR -> optimizeImmediateLoadAssociative(Opcode.ADD)
|
||||
Opcode.MULR -> optimizeImmediateLoadAssociative(Opcode.MUL)
|
||||
Opcode.MULSR -> optimizeImmediateLoadAssociative(Opcode.MULS)
|
||||
// Opcode.SUBR -> TODO("ir peephole Subr")
|
||||
// Opcode.DIVR -> TODO("ir peephole Divr")
|
||||
// Opcode.DIVSR -> TODO("ir peephole Divsr")
|
||||
// Opcode.MODR -> TODO("ir peephole Modr")
|
||||
// Opcode.DIVMODR -> TODO("ir peephole DivModr")
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,65 @@ smallringbuffer {
|
||||
}
|
||||
}
|
||||
|
||||
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
smallstack {
|
||||
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
|
||||
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
|
||||
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
ubyte[256] buffer
|
||||
ubyte sp = 255
|
||||
|
||||
sub init() {
|
||||
sp = 255
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
return 255-sp
|
||||
}
|
||||
|
||||
sub free() -> ubyte {
|
||||
return sp
|
||||
}
|
||||
|
||||
sub isfull() -> bool {
|
||||
return sp==0
|
||||
}
|
||||
|
||||
sub isempty() -> bool {
|
||||
return sp==255
|
||||
}
|
||||
|
||||
sub push(ubyte value) {
|
||||
; -- put a byte on the stack
|
||||
buffer[sp] = value
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pushw(uword value) {
|
||||
; -- put a word on the stack (lsb first then msb)
|
||||
buffer[sp] = lsb(value)
|
||||
sp--
|
||||
buffer[sp] = msb(value)
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; -- pops a byte off the stack
|
||||
sp++
|
||||
return buffer[sp]
|
||||
}
|
||||
|
||||
sub popw() -> uword {
|
||||
; -- pops a word off the stack.
|
||||
sp++
|
||||
cx16.r0H = buffer[sp]
|
||||
sp++
|
||||
cx16.r0L = buffer[sp]
|
||||
return cx16.r0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stack {
|
||||
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
|
||||
|
||||
@@ -358,7 +358,7 @@ extsub $FF71 = JMPFAR() clobbers(A,X) ; jump without r
|
||||
extsub $FF74 = INDFET(ubyte zpaddr @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> ubyte @A ; fetch byte from another bank (address in zp, ptr in A)
|
||||
extsub $FF77 = INDSTA(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) ; store byte to another bank (address in zp, ptr in $02b9)
|
||||
extsub $FF7A = INDCMP(ubyte value @A, ubyte bank @X, ubyte offset @Y) clobbers(X) -> bool @Pz, bool @Pc, bool @Pv; compare byte in another bank (address in zp, ptr in $02c8)
|
||||
extsub $FF7D = PRIMM() ; print immediate string
|
||||
extsub $FF7D = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
|
||||
|
||||
|
||||
; ---- C128 specific system utility routines: ----
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
FL_ONE_const .byte 129 ; 1.0
|
||||
FL_ZERO_const .byte 0,0,0,0,0 ; 0.0
|
||||
FL_LOG2_const .byte $80, $31, $72, $17, $f8 ; log(2)
|
||||
FL_65536_const .byte $91, $00, $00, $00, $00 ; 65536.0
|
||||
|
||||
|
||||
.section BSS
|
||||
floats_temp_var .byte ?,?,?,?,? ; temporary storage for a float
|
||||
@@ -126,6 +128,72 @@ cast_FAC1_as_w_into_ay .proc ; also used for float 2 b
|
||||
.pend
|
||||
|
||||
|
||||
cast_from_long .proc
|
||||
; convert long pointed to by AY into a float pointed to by R0
|
||||
; a bit slow algorithm implemented below: (msw(l) as word) as float * 65536.0 + (lsw(l) as float)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
sty P8ZP_SCRATCH_W1+1
|
||||
ldy #3
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ZP_SCRATCH_REG
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jsr GIVAYFAY
|
||||
lda #<FL_65536_const
|
||||
ldy #>FL_65536_const
|
||||
jsr CONUPK
|
||||
jsr FMULTT
|
||||
jsr pushFAC1
|
||||
ldy #1
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
sta P8ZP_SCRATCH_REG
|
||||
dey
|
||||
lda (P8ZP_SCRATCH_W1),y
|
||||
ldy P8ZP_SCRATCH_REG
|
||||
jsr GIVUAYFAY
|
||||
jsr MOVEF
|
||||
clc
|
||||
jsr popFAC
|
||||
jsr FADDT
|
||||
ldx cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jmp MOVMF
|
||||
.pend
|
||||
|
||||
cast_as_long .proc
|
||||
; convert float pointed to by R0 into a long pointed to by AY
|
||||
|
||||
FACHO = FAC_ADDR + 1
|
||||
|
||||
; save the target variable pointer on the stack
|
||||
pha
|
||||
tya
|
||||
pha
|
||||
|
||||
lda cx16.r0L
|
||||
ldy cx16.r0H
|
||||
jsr MOVFM
|
||||
jsr QINT
|
||||
|
||||
; restore the target variable pointer from the stack, and put the result in it
|
||||
pla
|
||||
sta P8ZP_SCRATCH_PTR+1
|
||||
pla
|
||||
sta P8ZP_SCRATCH_PTR
|
||||
|
||||
ldx #3
|
||||
ldy #0
|
||||
- lda FACHO,x
|
||||
sta (P8ZP_SCRATCH_PTR),y
|
||||
iny
|
||||
dex
|
||||
bpl -
|
||||
rts
|
||||
.pend
|
||||
|
||||
|
||||
|
||||
copy_float .proc
|
||||
; -- copies the 5 bytes of the mflt value pointed to by P8ZP_SCRATCH_W1,
|
||||
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||
|
||||
@@ -11,6 +11,7 @@ floats {
|
||||
; note: the fac1 and fac2 are working registers and take 6 bytes each,
|
||||
; floats in memory (and rom) are stored in 5-byte MFLPT packed format.
|
||||
|
||||
const uword FAC_ADDR = $61
|
||||
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
@@ -1313,6 +1313,8 @@ asmsub init_system() {
|
||||
lda #0
|
||||
sta c64.BGCOL0
|
||||
jsr disable_runstop_and_charsetswitch
|
||||
lda #$80
|
||||
sta 650 ; enable key repeat for normal keys too
|
||||
lda #PROG8_C64_BANK_CONFIG ; apply bank config
|
||||
sta $01
|
||||
and #1
|
||||
@@ -1349,6 +1351,8 @@ asmsub cleanup_at_exit() {
|
||||
jsr cbm.MEMTOP ; adjust MEMTOP down again
|
||||
jsr cbm.CLRCHN ; reset i/o channels
|
||||
jsr enable_runstop_and_charsetswitch
|
||||
lda #0
|
||||
sta 650 ; disable keyrepeats
|
||||
lda _exitcarry
|
||||
lsr a
|
||||
lda _exitcode
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -572,6 +572,85 @@ _try_iso
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub hex2long(str string @AY) -> long @R0R1_32 {
|
||||
; -- hexadecimal string (with or without '$') to long.
|
||||
; string may be in petscii or c64-screencode encoding.
|
||||
; stops parsing at the first character that's not a hex digit (except leading $)
|
||||
; result in R0:R1, number of characters processed also remains in cx16.r15L if you want to use it!! (0 = error)
|
||||
%asm {{
|
||||
sta P8ZP_SCRATCH_W2
|
||||
sty P8ZP_SCRATCH_W2+1
|
||||
ldy #0
|
||||
sty cx16.r0
|
||||
sty cx16.r0+1
|
||||
sty cx16.r0+2
|
||||
sty cx16.r0+3
|
||||
sty cx16.r15H
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
beq _stop
|
||||
cmp #'$'
|
||||
bne _loop
|
||||
iny
|
||||
_loop
|
||||
lda #0
|
||||
sta P8ZP_SCRATCH_B1
|
||||
lda (P8ZP_SCRATCH_W2),y
|
||||
beq _stop
|
||||
cmp #7 ; screencode letters A-F are 1-6
|
||||
bcc _add_letter
|
||||
and #127
|
||||
cmp #97
|
||||
bcs _try_iso ; maybe letter is iso:'a'-iso:'f' (97-102)
|
||||
cmp #'g'
|
||||
bcs _stop
|
||||
cmp #'a'
|
||||
bcs _add_letter
|
||||
cmp #'0'
|
||||
bcc _stop
|
||||
cmp #'9'+1
|
||||
bcs _stop
|
||||
_calc
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
asl cx16.r0
|
||||
rol cx16.r0+1
|
||||
rol cx16.r0+2
|
||||
rol cx16.r0+3
|
||||
and #$0f
|
||||
clc
|
||||
adc P8ZP_SCRATCH_B1
|
||||
ora cx16.r0
|
||||
sta cx16.r0
|
||||
iny
|
||||
bne _loop
|
||||
_stop
|
||||
sty cx16.r15L
|
||||
rts
|
||||
_add_letter
|
||||
pha
|
||||
lda #9
|
||||
sta P8ZP_SCRATCH_B1
|
||||
pla
|
||||
jmp _calc
|
||||
_try_iso
|
||||
cmp #103
|
||||
bcs _stop
|
||||
and #63
|
||||
bne _add_letter
|
||||
; !notreached!
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub bin2uword(str string @AY) -> uword @AY {
|
||||
; -- binary string (with or without '%') to uword.
|
||||
; stops parsing at the first character that's not a 0 or 1. (except leading %)
|
||||
|
||||
@@ -68,7 +68,65 @@ smallringbuffer {
|
||||
}
|
||||
}
|
||||
|
||||
; note: for a "small stack" (256 bytes size) just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
smallstack {
|
||||
; -- A small stack (LIFO) that uses just 256 bytes and is independent of the CPU stack. Stack is growing downward from the top of the buffer.
|
||||
; You can store and retrieve bytes and words. There are no guards against stack over/underflow.
|
||||
; note: for a "small stack" (256 bytes size) you might also perhaps just use the CPU stack via sys.push[w] / sys.pop[w].
|
||||
|
||||
ubyte[256] buffer
|
||||
ubyte sp = 255
|
||||
|
||||
sub init() {
|
||||
sp = 255
|
||||
}
|
||||
|
||||
sub size() -> ubyte {
|
||||
return 255-sp
|
||||
}
|
||||
|
||||
sub free() -> ubyte {
|
||||
return sp
|
||||
}
|
||||
|
||||
sub isfull() -> bool {
|
||||
return sp==0
|
||||
}
|
||||
|
||||
sub isempty() -> bool {
|
||||
return sp==255
|
||||
}
|
||||
|
||||
sub push(ubyte value) {
|
||||
; -- put a byte on the stack
|
||||
buffer[sp] = value
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pushw(uword value) {
|
||||
; -- put a word on the stack (lsb first then msb)
|
||||
buffer[sp] = lsb(value)
|
||||
sp--
|
||||
buffer[sp] = msb(value)
|
||||
sp--
|
||||
}
|
||||
|
||||
sub pop() -> ubyte {
|
||||
; -- pops a byte off the stack
|
||||
sp++
|
||||
return buffer[sp]
|
||||
}
|
||||
|
||||
sub popw() -> uword {
|
||||
; -- pops a word off the stack.
|
||||
sp++
|
||||
cx16.r0H = buffer[sp]
|
||||
sp++
|
||||
cx16.r0L = buffer[sp]
|
||||
return cx16.r0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
stack {
|
||||
; -- A stack (LIFO) that uses a block of 8 KB of memory. Growing downward from the top of the buffer.
|
||||
@@ -78,7 +136,7 @@ stack {
|
||||
ubyte bank
|
||||
|
||||
sub init(ubyte rambank) {
|
||||
; -- initialize the stack, must be called before use. Supply the HiRAM bank to use as buffer space.
|
||||
; -- initialize the stack, must be called before use. Supply the HIRAM bank to use as buffer space.
|
||||
sp = $bfff
|
||||
bank = rambank
|
||||
}
|
||||
@@ -157,7 +215,7 @@ ringbuffer {
|
||||
ubyte bank = 255 ; set via init()
|
||||
|
||||
sub init(ubyte rambank) {
|
||||
; -- initialize the ringbuffer, must be called before use. Supply the HiRAM bank to use as buffer space.
|
||||
; -- initialize the ringbuffer, must be called before use. Supply the HIRAM bank to use as buffer space.
|
||||
head = $a000
|
||||
tail = $bfff
|
||||
fill = 0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; **experimental** data compression/decompression routines, API subject to change!!
|
||||
; data compression/decompression routines
|
||||
|
||||
%import shared_compression
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -994,15 +999,12 @@ io_error:
|
||||
return false
|
||||
}
|
||||
|
||||
sub f_seek(uword pos_hiword, uword pos_loword) {
|
||||
sub f_seek(long position) {
|
||||
; -- seek in the reading file opened with f_open, to the given 32-bits position
|
||||
; Note: this will not work if you have already read the last byte of the file! Then you must close and reopen the file first.
|
||||
ubyte[6] command = ['p',0,0,0,0,0]
|
||||
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
|
||||
command[2] = lsb(pos_loword)
|
||||
command[3] = msb(pos_loword)
|
||||
command[4] = lsb(pos_hiword)
|
||||
command[5] = msb(pos_hiword)
|
||||
pokel(&command+2, position)
|
||||
cbm.SETNAM(sizeof(command), &command)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
@@ -1011,13 +1013,10 @@ io_error:
|
||||
}
|
||||
|
||||
|
||||
sub f_seek_w(uword pos_hiword, uword pos_loword) {
|
||||
sub f_seek_w(long position) {
|
||||
; -- seek in the output file opened with f_open_w_seek, to the given 32-bits position
|
||||
diskio.f_seek.command[1] = WRITE_IO_CHANNEL ; f_open_w uses this secondary address
|
||||
diskio.f_seek.command[2] = lsb(pos_loword)
|
||||
diskio.f_seek.command[3] = msb(pos_loword)
|
||||
diskio.f_seek.command[4] = lsb(pos_hiword)
|
||||
diskio.f_seek.command[5] = msb(pos_hiword)
|
||||
pokel(&diskio.f_seek.command+2, position)
|
||||
cbm.SETNAM(sizeof(diskio.f_seek.command), &diskio.f_seek.command)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
@@ -1025,54 +1024,63 @@ io_error:
|
||||
reset_write_channel() ; back to the write io channel
|
||||
}
|
||||
|
||||
asmsub f_tell() -> uword @R0, uword @R1, uword @R2, uword @R3 {
|
||||
; -- Returns the current read position of the opened read file,
|
||||
; in R0 and R1 (low + high words) and the file size in R2 and R3 (low + high words).
|
||||
; Returns 0 as size if the command is not supported by the DOS implementation/version.
|
||||
%asm {{
|
||||
jmp internal_f_tell
|
||||
}}
|
||||
}
|
||||
|
||||
sub internal_f_tell() {
|
||||
; gets the (32 bits) position + file size of the opened read file channel
|
||||
ubyte[2] command = ['t',0]
|
||||
command[1] = READ_IO_CHANNEL ; f_open uses this secondary address
|
||||
cbm.SETNAM(sizeof(command), &command)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
sub f_tell() -> long, long {
|
||||
; -- Returns the current read position of the opened read file, and the file size.
|
||||
; Returns 0,0 if the command is not supported by the DOS implementation/version.
|
||||
ubyte[3] command = ['?','?',0]
|
||||
bool success=false
|
||||
; valid response starts with "07," followed by hex notations of the position and filesize
|
||||
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
|
||||
cx16.r1 = read4hex()
|
||||
cx16.r0 = read4hex() ; position in R1:R0
|
||||
void cbm.CHRIN() ; separator space
|
||||
cx16.r3 = read4hex()
|
||||
cx16.r2 = read4hex() ; filesize in R3:R2
|
||||
success = true
|
||||
long filesize, filepos
|
||||
ubyte commandoffset
|
||||
|
||||
command[1] = 't'
|
||||
commandoffset = 1
|
||||
f_tell_internal()
|
||||
return filepos, filesize
|
||||
|
||||
sub f_tell_internal() {
|
||||
; this code is used for both the T (tell) and FL (fatlba) commands
|
||||
command[2] = READ_IO_CHANNEL ; f_open uses this secondary address
|
||||
cbm.SETNAM(sizeof(command)-commandoffset, &command+commandoffset)
|
||||
cbm.SETLFS(15, drivenumber, 15)
|
||||
void cbm.OPEN()
|
||||
void cbm.CHKIN(15) ; use #15 as input channel
|
||||
|
||||
; valid response starts with "07," followed by hex notations of the position and filesize
|
||||
if cbm.CHRIN()=='0' and cbm.CHRIN()=='7' and cbm.CHRIN()==',' {
|
||||
filepos = read8hex()
|
||||
void cbm.CHRIN() ; separator space
|
||||
filesize = read8hex()
|
||||
success = true
|
||||
} else {
|
||||
filesize = filepos = 0
|
||||
}
|
||||
|
||||
while cbm.READST()==0 {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
}
|
||||
|
||||
cbm.CLOSE(15)
|
||||
reset_read_channel() ; back to the read io channel
|
||||
}
|
||||
|
||||
while cbm.READST()==0 {
|
||||
cx16.r5L = cbm.CHRIN()
|
||||
if cx16.r5L=='\r' or cx16.r5L=='\n'
|
||||
break
|
||||
}
|
||||
|
||||
cbm.CLOSE(15)
|
||||
reset_read_channel() ; back to the read io channel
|
||||
if success
|
||||
return
|
||||
|
||||
cx16.r0 = cx16.r1 = cx16.r2 = cx16.r3 = 0
|
||||
}
|
||||
|
||||
sub read4hex() -> uword {
|
||||
str hex = "0000"
|
||||
hex[0] = cbm.CHRIN()
|
||||
hex[1] = cbm.CHRIN()
|
||||
hex[2] = cbm.CHRIN()
|
||||
hex[3] = cbm.CHRIN()
|
||||
return conv.hex2uword(hex)
|
||||
sub f_fatlba() -> long, long {
|
||||
; -- Return the current LBA, cluster number (sector index and shift value are ignored for now)
|
||||
diskio.f_tell.command[0] = 'f'
|
||||
diskio.f_tell.command[1] = 'l'
|
||||
diskio.f_tell.commandoffset = 0
|
||||
diskio.f_tell.f_tell_internal()
|
||||
return diskio.f_tell.filepos, diskio.f_tell.filesize
|
||||
}
|
||||
|
||||
|
||||
sub read8hex() -> long {
|
||||
str hex = "00000000"
|
||||
for cx16.r0L in 0 to 7 {
|
||||
hex[cx16.r0L] = cbm.CHRIN()
|
||||
}
|
||||
return conv.hex2long(hex)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,8 @@ floats {
|
||||
; note: fac1/2 might get clobbered even if not mentioned in the function's name.
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
const uword FAC_ADDR = $c3
|
||||
|
||||
extsub $fe00 = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 'facmo' and 'faclo', MSB FIRST. DON'T USE THIS, USE WRAPPER 'AYINT2' INSTEAD. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
|
||||
@@ -463,7 +463,7 @@ extsub $ff62 = screen_set_charset(ubyte charset @A, uword charsetptr @XY) clobb
|
||||
extsub $ff6e = JSRFAR() ; following word = address to call, byte after that=rom/ram bank it is in
|
||||
extsub $ff74 = fetch(ubyte zp_startaddr @A, ubyte bank @X, ubyte index @Y) clobbers(X) -> ubyte @A
|
||||
extsub $ff77 = stash(ubyte data @A, ubyte bank @X, ubyte index @Y) clobbers(X) ; note: The the zero page address containing the base address is passed in stavec ($03B2)
|
||||
extsub $ff7d = PRIMM()
|
||||
extsub $ff7d = PRIMM() ; print immediate string (the 0-terminated string immediately following the JSR PRIMM instruction)
|
||||
|
||||
; high level graphics & fonts
|
||||
extsub $ff20 = GRAPH_init(uword vectors @R0) clobbers(A,X,Y)
|
||||
@@ -620,6 +620,10 @@ const ubyte EXTAPI_scnsiz = $0D
|
||||
const ubyte EXTAPI_kbd_leds = $0E
|
||||
const ubyte EXTAPI_memory_decompress_from_func = $0F
|
||||
const ubyte EXTAPI_default_palette = $10
|
||||
const ubyte EXTAPI_has_machine_property = $11
|
||||
const ubyte EXTAPI_kbdbuf_get = $12
|
||||
const ubyte EXTAPI_kbdbuf_clear = $13
|
||||
const ubyte EXTAPI_blink_enable = $14
|
||||
|
||||
; extapi16 call numbers
|
||||
const ubyte EXTAPI16_test = $00
|
||||
@@ -676,6 +680,30 @@ sub mouse_present() -> bool {
|
||||
return cx16.r0L != $fc ; $fc = BAT_FAIL
|
||||
}
|
||||
|
||||
asmsub get_charset() -> ubyte @A {
|
||||
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
|
||||
%asm {{
|
||||
|
||||
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
|
||||
lda KERNAL_MODE
|
||||
beq _end
|
||||
|
||||
bit #$40 ;ISO mode flag
|
||||
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
|
||||
lda #1
|
||||
bra _end
|
||||
|
||||
+ bit #1 ;PETSCII upper case/graphics
|
||||
bne + ;usually KERNAL_MODE 2 or 4
|
||||
lda #2
|
||||
bra _end
|
||||
|
||||
+ lda #3 ;PETSCII upper/lower case
|
||||
_end:
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
; shims for the kernal routines called via the extapi call:
|
||||
|
||||
asmsub mouse_set_pos(uword xpos @R0, uword ypos @R1) clobbers(X) {
|
||||
@@ -758,31 +786,42 @@ asmsub set_default_palette() {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub get_charset() -> ubyte @A {
|
||||
;Get charset mode. result: 0=unknown, 1=ISO, 2=PETSCII upper case/gfx, 3=PETSCII lowercase.
|
||||
asmsub has_machine_property(ubyte property @X) clobbers(A,X) -> bool @Pc {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
lda #EXTAPI_has_machine_property
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
KERNAL_MODE = $0372 ; internal kernal variable, risky to read it, but it's ben stable for many releases.
|
||||
lda KERNAL_MODE
|
||||
beq _end
|
||||
asmsub kbdbuf_get() clobbers(X,Y) -> ubyte @A {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
lda #EXTAPI_kbdbuf_get
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
bit #$40 ;ISO mode flag
|
||||
beq + ;usually KERNAL_MODE 1 or 6 (| $40)
|
||||
lda #1
|
||||
bra _end
|
||||
asmsub kbdbuf_clear() clobbers(A) {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
lda #EXTAPI_kbdbuf_clear
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
+ bit #1 ;PETSCII upper case/graphics
|
||||
bne + ;usually KERNAL_MODE 2 or 4
|
||||
lda #2
|
||||
bra _end
|
||||
|
||||
+ lda #3 ;PETSCII upper/lower case
|
||||
_end:
|
||||
rts
|
||||
asmsub blink_enable(bool enable @X) clobbers(A,X) {
|
||||
; requires rom v49+
|
||||
%asm {{
|
||||
txa
|
||||
and #1
|
||||
eor #1
|
||||
tax
|
||||
lda #EXTAPI_blink_enable
|
||||
jmp cx16.extapi
|
||||
}}
|
||||
}
|
||||
|
||||
; TODO : implement shims for the remaining extapi calls.
|
||||
|
||||
|
||||
; ---- end of kernal routines ----
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -182,8 +182,8 @@ sub setclr (ubyte col, ubyte row, ubyte color) {
|
||||
}
|
||||
|
||||
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored) {
|
||||
; ---- set char at the given position on the screen. charcolor is ignored on PET
|
||||
sub setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor) {
|
||||
; ---- set char at the given position on the screen. NOTE: charcolor is ignored on PET
|
||||
%asm {{
|
||||
lda row
|
||||
asl a
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
; **experimental** data compression/decompression routines, API subject to change!!
|
||||
; data compression/decompression routines
|
||||
; This file contains the shared routines that work on all targets.
|
||||
|
||||
compression {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -279,6 +279,26 @@ sub hex2uword(str string) -> uword {
|
||||
}
|
||||
}
|
||||
|
||||
sub hex2long(str string) -> long {
|
||||
; -- hexadecimal string (with or without '$') to long.
|
||||
; stops parsing at the first character that's not a hex digit (except leading $)
|
||||
long result
|
||||
ubyte char
|
||||
if @(string)=='$'
|
||||
string++
|
||||
repeat {
|
||||
char = @(string)
|
||||
if char==0
|
||||
return result
|
||||
result <<= 4
|
||||
if char>='0' and char<='9'
|
||||
result |= char-'0'
|
||||
else
|
||||
result |= char-'a'+10
|
||||
string++
|
||||
}
|
||||
}
|
||||
|
||||
sub bin2uword(str string) -> uword {
|
||||
; -- binary string (with or without '%') to uword.
|
||||
; stops parsing at the first character that's not a 0 or 1. (except leading %)
|
||||
|
||||
@@ -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
|
||||
}}
|
||||
|
||||
@@ -60,13 +60,13 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val quietAll by cli.option(ArgType.Boolean, fullName = "quiet", description = "don't print compiler and assembler messages, except warnings and errors")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler messages")
|
||||
val slabsGolden by cli.option(ArgType.Boolean, fullName = "slabsgolden", description = "put memory() slabs in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val slabsHighBank by cli.option(ArgType.Int, fullName = "slabshigh", description = "put memory() slabs in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
|
||||
val dontIncludeSourcelines by cli.option(ArgType.Boolean, fullName = "nosourcelines", description = "do not include original Prog8 source lines in generated asm code")
|
||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler (one of ${CompilationTargets.joinToString(",")} or a custom target properties file) (required)")
|
||||
val showTimings by cli.option(ArgType.Boolean, fullName = "timings", description = "show internal compiler timings (for performance analysis)")
|
||||
val varsGolden by cli.option(ArgType.Boolean, fullName = "varsgolden", description = "put uninitialized variables in 'golden ram' memory area instead of at the end of the program. On the cx16 target this is $0400-07ff. This is unavailable on other systems.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.")
|
||||
val varsHighBank by cli.option(ArgType.Int, fullName = "varshigh", description = "put uninitialized variables in high memory area instead of at the end of the program. On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.")
|
||||
val startVm by cli.option(ArgType.Boolean, fullName = "vm", description = "run a .p8ir IR source file in the embedded VM")
|
||||
val warnSymbolShadowing by cli.option(ArgType.Boolean, fullName = "warnshadow", description="show assembler warnings about symbol shadowing")
|
||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watch for file changes)")
|
||||
@@ -145,7 +145,7 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
}
|
||||
|
||||
if(varsHighBank==0 && compilationTarget==Cx16Target.NAME) {
|
||||
System.err.println("On the Commander X16, HiRAM bank 0 is used by the kernal and can't be used.")
|
||||
System.err.println("On the Commander X16, HIRAM bank 0 is used by the kernal and can't be used.")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -497,8 +497,8 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("parameter '${param.first.name}' should be (u)byte or bool", param.first.position)
|
||||
}
|
||||
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && param.first.type!=DataType.pointer(BaseDataType.UBYTE)) {
|
||||
err("parameter '${param.first.name}' should be (u)word, str or ^^ubyte")
|
||||
if (!param.first.type.isWord && !param.first.type.isString && !param.first.type.isArray && !param.first.type.isPointer) {
|
||||
err("parameter '${param.first.name}' should be (u)word, str or a pointer")
|
||||
}
|
||||
}
|
||||
else if(param.second.statusflag!=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)
|
||||
}
|
||||
@@ -2081,7 +2093,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(memread: DirectMemoryRead) {
|
||||
if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
|
||||
if(memread.addressExpression.inferType(program).isLong)
|
||||
errors.err("long address not yet supported", memread.addressExpression.position)
|
||||
else if(!allowedMemoryAccessAddressExpression(memread.addressExpression, program))
|
||||
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memread.position)
|
||||
|
||||
val pointervar = memread.addressExpression as? IdentifierReference
|
||||
@@ -2114,7 +2128,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
override fun visit(memwrite: DirectMemoryWrite) {
|
||||
if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
|
||||
if(memwrite.addressExpression.inferType(program).isLong)
|
||||
errors.err("long address not yet supported", memwrite.addressExpression.position)
|
||||
else if(!allowedMemoryAccessAddressExpression(memwrite.addressExpression, program))
|
||||
errors.err("invalid address type for memory access, expected ^^ubyte or just uword", memwrite.position)
|
||||
|
||||
val pointervar = memwrite.addressExpression as? IdentifierReference
|
||||
|
||||
@@ -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),
|
||||
@@ -339,8 +339,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
|
||||
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
|
||||
val dt = memread.addressExpression.inferType(program).getOr(DataType.UWORD)
|
||||
if(dt.isLong) {
|
||||
val constAddr = memread.addressExpression.constValue(program)?.number
|
||||
if(constAddr==null || constAddr>=65536)
|
||||
return noModifications
|
||||
}
|
||||
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
|
||||
return noModifications
|
||||
|
||||
@@ -354,8 +359,13 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
||||
// make sure the memory address is an uword or a pointer (to whatever type), otherwise cast
|
||||
// make sure the memory address is an uword or long or a pointer (to whatever type), otherwise cast
|
||||
val dt = memwrite.addressExpression.inferType(program).getOr(DataType.UWORD)
|
||||
if(dt.isLong) {
|
||||
val constAddr = memwrite.addressExpression.constValue(program)?.number
|
||||
if(constAddr==null || constAddr>=65536)
|
||||
return noModifications
|
||||
}
|
||||
if(dt.isUndefined || dt.isUnsignedWord || dt.isPointer)
|
||||
return noModifications
|
||||
|
||||
|
||||
@@ -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}")
|
||||
@@ -469,18 +480,31 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
|
||||
override fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> {
|
||||
if(addressOf.arrayIndex!=null) {
|
||||
val tgt = addressOf.identifier?.constValue(program)
|
||||
if (tgt != null && tgt.type.isWord) {
|
||||
// &constant[idx] --> constant + idx
|
||||
val indexExpr = addressOf.arrayIndex!!.indexExpr
|
||||
val right = if(indexExpr.inferType(program) issimpletype tgt.type)
|
||||
indexExpr
|
||||
else
|
||||
TypecastExpression(indexExpr, DataType.forDt(tgt.type), true, indexExpr.position)
|
||||
val add = BinaryExpression(tgt, "+", right, addressOf.position)
|
||||
return listOf(
|
||||
IAstModification.ReplaceNode(addressOf, add, parent)
|
||||
)
|
||||
val tgt = addressOf.identifier
|
||||
if(tgt!=null) {
|
||||
val constAddress = tgt.constValue(program)
|
||||
if (constAddress != null && constAddress.type.isInteger) {
|
||||
// &constant[idx] --> constant + idx
|
||||
val indexExpr = addressOf.arrayIndex!!.indexExpr
|
||||
val right = if (indexExpr.inferType(program) issimpletype constAddress.type)
|
||||
indexExpr
|
||||
else
|
||||
TypecastExpression(indexExpr, DataType.forDt(constAddress.type), true, indexExpr.position)
|
||||
val add = BinaryExpression(constAddress, "+", right, addressOf.position)
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
|
||||
} else {
|
||||
val decl = tgt.targetVarDecl()
|
||||
if(decl!=null && decl.datatype.isInteger) {
|
||||
// &addressvar[idx] --> addressvar + idx
|
||||
val indexExpr = addressOf.arrayIndex!!.indexExpr
|
||||
val right = if (indexExpr.inferType(program) istype decl.datatype)
|
||||
indexExpr
|
||||
else
|
||||
TypecastExpression(indexExpr, decl.datatype, true, indexExpr.position)
|
||||
val add = BinaryExpression(tgt, "+", right, addressOf.position)
|
||||
return listOf(IAstModification.ReplaceNode(addressOf, add, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
|
||||
@@ -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 }
|
||||
|
||||
@@ -16,10 +16,7 @@ import prog8.ast.expressions.PrefixExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.source.SourceCode
|
||||
import prog8.code.target.C128Target
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.PETTarget
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.code.target.*
|
||||
import prog8tests.helpers.*
|
||||
|
||||
|
||||
@@ -438,4 +435,24 @@ main {
|
||||
errors.errors[0] shouldContain "8:9: cannot assign to an array or string that is located in ROM"
|
||||
errors.errors[1] shouldContain "9:9: cannot assign to an array or string that is located in ROM"
|
||||
}
|
||||
|
||||
test("long addresses not yet supported in memread and memwrite") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const long bufferll = 888888
|
||||
long @shared addrlv = 777777
|
||||
|
||||
cx16.r0L = bufferll[2]
|
||||
cx16.r1L = @(999999)
|
||||
cx16.r2L = @(addrlv)
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(Cx16Target(), true, src, outputDir, errors = errors, writeAssembly = false) shouldBe null
|
||||
errors.errors.size shouldBe 3
|
||||
errors.errors[0] shouldContain "long address not yet supported"
|
||||
errors.errors[1] shouldContain "long address not yet supported"
|
||||
errors.errors[2] shouldContain "long address not yet supported"
|
||||
}
|
||||
})
|
||||
@@ -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
|
||||
@@ -305,30 +306,38 @@ main {
|
||||
(waDeclV as NumericLiteral).number shouldBe 40960.0
|
||||
}
|
||||
|
||||
test("address of a const uword pointer array expression") {
|
||||
val src= """
|
||||
test("address of a const integer pointer array expression") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const uword buffer = 2000
|
||||
const long bufferl = 999999
|
||||
uword @shared addr = &buffer[2]
|
||||
|
||||
const ubyte width = 100
|
||||
ubyte @shared i
|
||||
ubyte @shared j
|
||||
uword @shared addr2 = &buffer[i * width + j]
|
||||
long @shared addr2 = &bufferl[2]
|
||||
const uword width = 100
|
||||
uword @shared addr3 = &buffer[width]
|
||||
long @shared addr4 = &bufferl[width]
|
||||
}
|
||||
}"""
|
||||
val result = compileText(Cx16Target(), true, src, outputDir, writeAssembly = true)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 11
|
||||
val assignAddr = (st[2] as Assignment).value
|
||||
(assignAddr as NumericLiteral).number shouldBe 2002.0
|
||||
val assignAddr2 = (st[9] as Assignment).value as BinaryExpression
|
||||
assignAddr2.operator shouldBe "+"
|
||||
st.size shouldBe 12
|
||||
val assignAddr = (st[3] as Assignment).value as NumericLiteral
|
||||
assignAddr.number shouldBe 2002.0
|
||||
assignAddr.type shouldBe BaseDataType.UWORD
|
||||
val assignAddr2 = (st[5] as Assignment).value as NumericLiteral
|
||||
assignAddr2.number shouldBe 1000001.0
|
||||
assignAddr2.type shouldBe BaseDataType.LONG
|
||||
val assignAddr3 = (st[8] as Assignment).value as NumericLiteral
|
||||
assignAddr3.number shouldBe 2100
|
||||
assignAddr3.type shouldBe BaseDataType.UWORD
|
||||
val assignAddr4 = (st[10] as Assignment).value as NumericLiteral
|
||||
assignAddr4.number shouldBe 1000099.0
|
||||
assignAddr4.type shouldBe BaseDataType.LONG
|
||||
}
|
||||
|
||||
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 +348,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 +357,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 +368,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 +377,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 +388,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 +408,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 +464,7 @@ main {
|
||||
}
|
||||
|
||||
test("const long with small values") {
|
||||
val src="""
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
const long notkaputt = 42
|
||||
@@ -464,4 +473,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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,14 +572,15 @@ data class AddressOf(var identifier: IdentifierReference?, var arrayIndex: Array
|
||||
val index = arrayIndex?.constIndex()
|
||||
if (index != null) {
|
||||
address += when {
|
||||
target.datatype.isUnsignedWord -> index
|
||||
target.datatype.isInteger -> index
|
||||
target.datatype.isArray -> program.memsizer.memorySize(targetVar.datatype, index)
|
||||
else -> throw FatalAstException("need array or uword ptr")
|
||||
else -> throw FatalAstException("need array or ptr")
|
||||
}
|
||||
} else
|
||||
return null
|
||||
}
|
||||
return NumericLiteral(BaseDataType.UWORD, address, position)
|
||||
val addressType = if(targetVar.datatype.isLong) BaseDataType.LONG else BaseDataType.UWORD
|
||||
return NumericLiteral(addressType, address, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -946,7 +947,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 {
|
||||
@@ -1415,14 +1417,14 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
}
|
||||
|
||||
// the value of a variable can (temporarily) be a different type as the vardecl itself.
|
||||
// don't return the value if the types don't match yet!
|
||||
val value = vardecl.value?.constValue(program)
|
||||
if(value==null || value.type==vardecl.datatype.base)
|
||||
return value
|
||||
val optimal = NumericLiteral.optimalNumeric(value.number, value.position)
|
||||
if(optimal.type==vardecl.datatype.base)
|
||||
return optimal
|
||||
return null
|
||||
val casted = value.cast(vardecl.datatype.base, true)
|
||||
return if(casted.isValid)
|
||||
casted.valueOrZero()
|
||||
else
|
||||
null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
@@ -1765,6 +1767,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 +1849,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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -45,6 +45,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -99,6 +113,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -293,7 +308,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -825,6 +842,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
|
||||
|
||||
@@ -45,6 +45,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -99,6 +113,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -208,6 +223,7 @@ LIBRARY MODULE NAME: floats
|
||||
floats {
|
||||
const float E
|
||||
const float EPSILON
|
||||
const uword FAC_ADDR
|
||||
const float PI
|
||||
const float TWOPI
|
||||
const float π
|
||||
@@ -420,7 +436,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -938,6 +956,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
|
||||
|
||||
@@ -84,6 +84,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
ubyte bank
|
||||
uword sp
|
||||
@@ -137,6 +151,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -216,20 +231,20 @@ diskio {
|
||||
exists (str filename) -> bool
|
||||
f_close ()
|
||||
f_close_w ()
|
||||
f_fatlba () -> long, long
|
||||
f_open (str filenameptr) -> bool
|
||||
f_open_w (str filename) -> bool
|
||||
f_open_w_seek (str filename) -> bool
|
||||
f_read (uword bufferpointer, uword num_bytes) -> uword
|
||||
f_read_all (uword bufferpointer) -> uword
|
||||
f_readline (^^ubyte bufptr @AY) clobbers (X) -> ubyte @Y, ubyte @A
|
||||
f_seek (uword pos_hiword, uword pos_loword)
|
||||
f_seek_w (uword pos_hiword, uword pos_loword)
|
||||
f_tell () -> uword @R0, uword @R1, uword @R2, uword @R3
|
||||
f_seek (long position)
|
||||
f_seek_w (long position)
|
||||
f_tell () -> long, long
|
||||
f_write (uword bufferpointer, uword num_bytes) -> bool
|
||||
fastmode (ubyte mode) -> bool
|
||||
get_loadaddress (str filename) -> uword
|
||||
internal_f_open_w (str filename, bool open_for_seeks) -> bool
|
||||
internal_f_tell ()
|
||||
internal_load_routine (str filenameptr, uword address_override, bool headerless) -> uword
|
||||
internal_save_routine (str filenameptr, uword startaddress, uword savesize, bool headerless) -> bool
|
||||
lf_end_list ()
|
||||
@@ -240,10 +255,10 @@ 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
|
||||
read8hex () -> long
|
||||
relabel (str name)
|
||||
rename (str oldfileptr, str newfileptr)
|
||||
reset_read_channel ()
|
||||
@@ -300,6 +315,7 @@ LIBRARY MODULE NAME: floats
|
||||
floats {
|
||||
const float E
|
||||
const float EPSILON
|
||||
const uword FAC_ADDR
|
||||
const float PI
|
||||
const float TWOPI
|
||||
const float π
|
||||
@@ -732,7 +748,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -835,13 +853,17 @@ cx16 {
|
||||
const ubyte EXTAPI16_stack_pop
|
||||
const ubyte EXTAPI16_stack_push
|
||||
const ubyte EXTAPI16_test
|
||||
const ubyte EXTAPI_blink_enable
|
||||
const ubyte EXTAPI_clear_status
|
||||
const ubyte EXTAPI_cursor_blink
|
||||
const ubyte EXTAPI_default_palette
|
||||
const ubyte EXTAPI_getlfs
|
||||
const ubyte EXTAPI_has_machine_property
|
||||
const ubyte EXTAPI_iso_cursor_char
|
||||
const ubyte EXTAPI_joystick_ps2_keycodes
|
||||
const ubyte EXTAPI_kbd_leds
|
||||
const ubyte EXTAPI_kbdbuf_clear
|
||||
const ubyte EXTAPI_kbdbuf_get
|
||||
const ubyte EXTAPI_led_update
|
||||
const ubyte EXTAPI_memory_decompress_from_func
|
||||
const ubyte EXTAPI_mouse_set_position
|
||||
@@ -1141,6 +1163,7 @@ cx16 {
|
||||
bas_psgnote (ubyte voice @A, ubyte note @X, ubyte fracsemitone @Y) clobbers (A,X,Y) -> bool @Pc @bank 10 = $c012
|
||||
bas_psgplaystring (ubyte length @A, str string @XY) clobbers (A,X,Y) @bank 10 = $c018
|
||||
bas_psgwav (ubyte voice @A, ubyte waveform @X) clobbers (A,X,Y) -> bool @Pc @bank 10 = $c015
|
||||
blink_enable (bool enable @X) clobbers (A,X)
|
||||
clock_get_date_time () clobbers (A,X,Y) -> uword @R0, uword @R1, uword @R2, uword @R3 = $ff50
|
||||
clock_set_date_time (uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, uword jiffiesweekday @R3) clobbers (A,X,Y) = $ff4d
|
||||
console_get_char () clobbers (X,Y) -> ubyte @A = $fee1
|
||||
@@ -1163,6 +1186,7 @@ cx16 {
|
||||
getlfs () -> ubyte @X, ubyte @A, ubyte @Y
|
||||
getrambank () -> ubyte @A
|
||||
getrombank () -> ubyte @A
|
||||
has_machine_property (ubyte property @X) clobbers (A,X) -> bool @Pc
|
||||
i2c_batch_read (ubyte device @X, uword buffer @R0, uword length @R1, bool advance @Pc) clobbers (A,Y) -> bool @Pc = $feb4
|
||||
i2c_batch_write (ubyte device @X, uword buffer @R0, uword length @R1, bool advance @Pc) clobbers (A,Y) -> bool @Pc = $feb7
|
||||
i2c_read_byte (ubyte device @X, ubyte offset @Y) clobbers (X,Y) -> ubyte @A, bool @Pc = $fec6
|
||||
@@ -1172,6 +1196,8 @@ cx16 {
|
||||
joystick_scan () clobbers (A,X,Y) = $ff53
|
||||
joysticks_detect () -> ubyte
|
||||
joysticks_getall (bool also_keyboard_js) -> uword
|
||||
kbdbuf_clear () clobbers (A)
|
||||
kbdbuf_get () clobbers (X,Y) -> ubyte @A
|
||||
kbdbuf_get_modifiers () -> ubyte @A = $fec0
|
||||
kbdbuf_peek () -> ubyte @A, ubyte @X = $febd
|
||||
kbdbuf_put (ubyte key @A) clobbers (X) = $fec3
|
||||
@@ -1422,6 +1448,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 ()
|
||||
|
||||
@@ -45,6 +45,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -99,6 +113,7 @@ conv {
|
||||
ubyte[] @shared string_out
|
||||
any2uword (str string @AY) -> uword @AY, ubyte @X
|
||||
bin2uword (str string @AY) -> uword @AY
|
||||
hex2long (str string @AY) -> long @R0R1_32
|
||||
hex2uword (str string @AY) -> uword @AY
|
||||
internal_byte2decimal (byte value @A) -> ubyte @Y, ubyte @A, ubyte @X
|
||||
internal_ubyte2decimal (ubyte value @A) -> ubyte @Y, ubyte @X, ubyte @A
|
||||
@@ -244,7 +259,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target @R0, str suffix @R1, ubyte maxlength @X) clobbers (Y) -> ubyte @A
|
||||
ncompare (str string1 @R0, str string2 @AY, ubyte length @X) clobbers (X,Y) -> byte @A
|
||||
ncopy (str source @R0, str target @AY, ubyte maxlength @X) clobbers (A,X) -> ubyte @Y
|
||||
pattern_match (str string @AY, str pattern @R0) clobbers (Y) -> bool @A
|
||||
rfind (str string @AY, ubyte character @X) -> ubyte @A, bool @Pc
|
||||
right (str source @AY, ubyte length @X, str target @R1) clobbers (A,Y)
|
||||
@@ -537,9 +554,10 @@ txt {
|
||||
scroll_left () clobbers (A,X,Y)
|
||||
scroll_right () clobbers (A,X)
|
||||
scroll_up () clobbers (A,X)
|
||||
setcc (ubyte col, ubyte row, ubyte character, ubyte charcolor_ignored)
|
||||
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, ubyte row, ubyte color)
|
||||
size () clobbers (A) -> ubyte @X, ubyte @Y
|
||||
spc ()
|
||||
uppercase ()
|
||||
waitkey () -> ubyte @A
|
||||
|
||||
@@ -25,6 +25,20 @@ smallringbuffer {
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
smallstack {
|
||||
ubyte[] buffer
|
||||
ubyte sp
|
||||
free () -> ubyte
|
||||
init ()
|
||||
isempty () -> bool
|
||||
isfull () -> bool
|
||||
pop () -> ubyte
|
||||
popw () -> uword
|
||||
push (ubyte value)
|
||||
pushw (uword value)
|
||||
size () -> ubyte
|
||||
}
|
||||
|
||||
stack {
|
||||
uword buffer_ptr
|
||||
uword sp
|
||||
@@ -75,6 +89,7 @@ conv {
|
||||
str string_out
|
||||
any2uword (str string) -> uword, ubyte
|
||||
bin2uword (str string) -> uword
|
||||
hex2long (str string) -> long
|
||||
hex2uword (str string) -> uword
|
||||
internal_str_ub (ubyte value, str out_ptr)
|
||||
internal_str_uw (uword value, str out_ptr)
|
||||
@@ -311,7 +326,9 @@ strings {
|
||||
lstripped (str s) -> str
|
||||
ltrim (str s)
|
||||
ltrimmed (str s) -> str
|
||||
nappend (str target, str suffix, ubyte maxlength) -> ubyte
|
||||
ncompare (str st1, str st2, ubyte length) -> byte
|
||||
ncopy (str source, str target, ubyte maxlength) -> ubyte
|
||||
rfind (str stringptr, ubyte character) -> ubyte, bool
|
||||
right (str source, ubyte slen, str target)
|
||||
rstrip (str s)
|
||||
@@ -559,6 +576,7 @@ txt {
|
||||
rvs_off ()
|
||||
rvs_on ()
|
||||
setchr (ubyte col, ubyte row, ubyte char)
|
||||
size () -> ubyte, ubyte
|
||||
spc ()
|
||||
uppercase ()
|
||||
width () -> ubyte
|
||||
|
||||
@@ -18,7 +18,7 @@ The language
|
||||
- Identifiers and string literals can contain non-ASCII characters so for example ``knäckebröd`` and ``見せしめ`` are valid identifiers.
|
||||
- There's usually a single statement per line. There is no statement separator.
|
||||
- Semicolon ``;`` is used to start a line comment. Multi-line comments are also possible by enclosing it all in ``/*`` and ``*/``.
|
||||
- Ternary operator ``x ? value1 : value2`` is available in the form of an *if-expression*: ``if x value1 else value2``
|
||||
- Ternary operator ``x ? value1 : value2`` is available in the form of an *if-expression*: ``if x [then] value1 else value2``
|
||||
- There's a Swift/Zig/Go style ``defer`` statement for delayed cleanup is available in the subroutine scope.
|
||||
- Qualified names are searched from within the top level namespace (so you have to provide the full qualified name). Unqualified names are locally scoped.
|
||||
- A trailing comma is allowed optionally in array literals: [1,2,3,] is a valid array of values 1, 2 and 3.
|
||||
|
||||
@@ -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.
|
||||
@@ -227,7 +228,7 @@ One or more .p8 module files
|
||||
|
||||
``-slabshigh``
|
||||
put memory() slabs in high memory area instead of at the end of the program.
|
||||
On the cx16 target the value specifies the HiRAM bank to use, on other systems this value is ignored.
|
||||
On the cx16 target the value specifies the HIRAM bank to use, on other systems this value is ignored.
|
||||
|
||||
``-nosourcelines``
|
||||
Do not include the original prog8 source code lines as comments in the generated assembly code file,
|
||||
@@ -262,7 +263,7 @@ One or more .p8 module files
|
||||
|
||||
c64: $C000 - $CFFF ; 4 kB, and the specified rambank number is ignored
|
||||
|
||||
cx16: $A000 - $BFFF ; 8 kB in the specified HiRAM bank (note: no auto bank switching is done, you must make sure yourself that this HiRAM bank is active when accessing these variables!)
|
||||
cx16: $A000 - $BFFF ; 8 kB in the specified HIRAM bank (note: no auto bank switching is done, you must make sure yourself that this HIRAM bank is active when accessing these variables!)
|
||||
|
||||
If you use this option, you can no longer use the part of the above memory area that is
|
||||
alotted to the variables, for your own purposes. The output of the 64tass assembler step at the
|
||||
|
||||
@@ -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.
|
||||
@@ -200,3 +200,7 @@ If multiple options are listed above, you can select which one you want to launc
|
||||
**Syntax highlighting:** for a few different editors, syntax highlighting definition files are provided.
|
||||
Look in the :source:`syntax-files <syntax-files>` directory in the github repository to find them.
|
||||
|
||||
**CTags:** for editors that support them, such as Vim, there is an effort by a member of the cummunity to provide "ctags" files for Prog8.
|
||||
The basic purpose of ctags is to index the definitions from your source code and let you find them easily.
|
||||
It is not really to find each use of print for example, but to find where print is defined.
|
||||
Visit `the Github repository <https://github.com/gillham/prog8ctags>`_ for the files and detailed usage information.
|
||||
+453
-365
File diff suppressed because it is too large.
Load diff
@@ -735,8 +735,8 @@ if expression
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Similar to the if statement, but this time selects one of two possible values as the outcome of the expression,
|
||||
depending on the condition. You write it as ``if <condition> <value1> else <value2>`` and it can be
|
||||
used anywhere an expression is used to assign or pass a value.
|
||||
depending on the condition. You write it as ``if <condition> [then] <value1> else <value2>`` (the then keyword is optional)
|
||||
and it can be used anywhere an expression is used to assign or pass a value.
|
||||
The first value will be used if the condition is true, otherwise the second value is used.
|
||||
Sometimes it may be more legible if you surround the condition expression with parentheses so it is better
|
||||
separated visually from the first value following it.
|
||||
@@ -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.
|
||||
|
||||
@@ -357,4 +357,12 @@ There are a few things to note:
|
||||
|
||||
.. note::
|
||||
The ROMable code generation is still quite experimental. Problems may still surface, and perhaps
|
||||
stuff will change a bit in a future compiler version.
|
||||
stuff will change a bit in a future compiler version.
|
||||
|
||||
|
||||
|
||||
Formal ANTLR4 syntax and grammar definition
|
||||
-------------------------------------------
|
||||
|
||||
.. literalinclude :: ../../parser/src/main/antlr/Prog8ANTLR.g4
|
||||
:language: antlr
|
||||
+31
-91
@@ -1,56 +1,48 @@
|
||||
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)
|
||||
- before final release: test all examples and programs again with final version of the compiler!
|
||||
|
||||
|
||||
Future Things and Ideas
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
- make $8000000 a valid long integer (-2147483648) this is more involved than you think. To make this work: long \|= $80000000
|
||||
- add cx16.r0r1sL, r2r3sL, ... etc to map signed longs on the virtual registers?
|
||||
- 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)
|
||||
- array-as-param bug: printf([1111,2,3,-4444]) gives argument type mismatch (param is of type uword), while printf([1111,2,3,4444]) just works fine (passes address of @nosplit array)
|
||||
- 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?)
|
||||
- allow memory() to occur in array initializer (maybe needed for 2 dimensional arrays?) i.e. make it a constant (see github issue #192)
|
||||
- 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.
|
||||
- After long variable type is completed: make all constants long by default? or not? (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.
|
||||
Can give descriptive error message for old syntax that still includes the type name?
|
||||
- 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?)
|
||||
- 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" ?
|
||||
- enums?
|
||||
- fix the c64 multiplexer example
|
||||
- romable: should we have a way to explicitly set the memory address for the BSS area (add a -varsaddress and -slabsaddress options?)
|
||||
- romable: fix remaining codegens (some for loops, see ForLoopsAsmGen)
|
||||
- Kotlin: can we use inline value classes in certain spots? (domain types instead of primitives)
|
||||
- add float support to the configurable compiler targets
|
||||
- add float support to the configurable compiler targets. Restrictions: just have "cbm-style floats" as an option (to that it can slot into the current float codegen), where all you have to specify is the addresses of AYINT and GIVAYF and FADDT and all their friends.
|
||||
- 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!)
|
||||
@@ -61,12 +53,15 @@ Future Things and Ideas
|
||||
(this is already done hardcoded for several of the builtin functions)
|
||||
- more support for (64tass) SEGMENTS in the prog8 syntax itself? maybe %segment blah in blocks?
|
||||
- ability to use a sub instead of only a var for @bank ? what for though? dynamic bank/overlay loading?
|
||||
- enums?
|
||||
- Zig-like try-based error handling where the V flag could indicate error condition? and/or BRK to jump into monitor on failure? (has to set BRK vector for that) But the V flag is also set on certain normal instructions
|
||||
|
||||
|
||||
IR/VM
|
||||
-----
|
||||
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- optimize bool b = sgn(value)<0: still does a compare with 0 even though SGN sets all status bits. What is the code when a BIT instruction is used?
|
||||
- optimize float<0 float==0 float>0 to use SGN instruction? Check what code is generated for other data types.
|
||||
- getting it in shape for code generation: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||
- fix call() return value handling (... what's wrong with it again?)
|
||||
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||
- is it possible to use LOADFIELD/STOREFIELD instructions even more?
|
||||
@@ -99,73 +94,14 @@ IR/VM
|
||||
- implement fast code paths for TODO("inplace split....
|
||||
- implement more TODOs in AssignmentGen
|
||||
- do something with the 'split' tag on split word arrays
|
||||
- add more optimizations in IRPeepholeOptimizer
|
||||
- add more optimizations in IRPeepholeOptimizer, implement the TODOs in there at least
|
||||
- extend the index range from 0-255 to 0-32767 in the LOADX, STOREX, LOADFIELD, STOREFIELD etc instructions (not compatible with 8 bit 6502, but the 68000 can use that)
|
||||
- idea: replace all scalar variables that are not @shared by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||
global initialization values are simply a list of LOAD instructions.
|
||||
Variables replaced include all subroutine parameters? Or not? So the only variables that remain as variables are arrays and strings.
|
||||
- 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
|
||||
@@ -179,17 +115,21 @@ Libraries
|
||||
Optimizations
|
||||
-------------
|
||||
|
||||
- change float<0, float==0, float>0 to use sgn(float) instead? (also see IR)
|
||||
- optimize inplaceLongShiftRight() for byte aligned cases
|
||||
- more optimized operator handling of different types, for example uword a ^ byte b now does a type cast of b to word first
|
||||
- optimize longEqualsValue() for const and variable operands to not assign needlessly to R0-R3.
|
||||
- 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
|
||||
- Optimize the IfExpression code generation to be more like regular if-else code. (both 6502 and IR) search for "TODO don't store condition as expression"
|
||||
- optimize floats.cast_from_long and floats.cast_as_long by directly accessing FAC bits?
|
||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
|
||||
This will probably need the register categorization from the IR explained there, for the old 6502 codegen there is not enough information to act on
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -33,7 +33,7 @@ main {
|
||||
|
||||
txt.print("\nseeking to 1292 and writing a few bytes...\n")
|
||||
if diskio.f_open_w_seek("seektestfile.bin") {
|
||||
diskio.f_seek_w(0, 1292)
|
||||
diskio.f_seek_w(1292)
|
||||
void diskio.f_write("123", 3)
|
||||
diskio.f_close_w()
|
||||
} else {
|
||||
@@ -66,7 +66,7 @@ main {
|
||||
; now the actual seek and read of the last few bytes
|
||||
txt.print("\nseeking to 1290 and reading...\n")
|
||||
if diskio.f_open("seektestfile.bin") {
|
||||
diskio.f_seek(0, 1290)
|
||||
diskio.f_seek(1290)
|
||||
uword ptr = megabuffer
|
||||
do {
|
||||
size = diskio.f_read(ptr, 255)
|
||||
|
||||
@@ -13,7 +13,7 @@ The ZSMKIT-A000.BIN included here is release version 2.6 of zsmkit.
|
||||
|
||||
DEMO PROGRAM
|
||||
------------
|
||||
The demo program loads the zsmkit player program in HiRAM bank 1.
|
||||
The demo program loads the zsmkit player program in HIRAM bank 1.
|
||||
The ZSM music file is read into memory too starting from bank 2.
|
||||
The zmskit library routines are defined in zsmkit.p8 as extsubs,
|
||||
using prog8's automatic ram banking mechanism.
|
||||
@@ -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() {
|
||||
|
||||
+77
-31
@@ -1,41 +1,87 @@
|
||||
%import floats
|
||||
%import textio
|
||||
%import math
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
long @shared lv, lv2
|
||||
long @shared lv = -1
|
||||
word @shared wv = -1
|
||||
byte @shared bv = -1
|
||||
float @shared fv = -1.1
|
||||
bool b1, b2, b3, b4 = false
|
||||
|
||||
lv = $11111111
|
||||
lv2 = $55555555
|
||||
b1 = bv<0
|
||||
b2 = wv<0
|
||||
b3 = lv<0
|
||||
b4 = fv<0
|
||||
txt.print_bool(b1)
|
||||
txt.print_bool(b2)
|
||||
txt.print_bool(b3)
|
||||
txt.print_bool(b4)
|
||||
txt.nl()
|
||||
|
||||
lv = lv | lv2 ^ 999999
|
||||
b1=b2=b3=b4=false
|
||||
b1 = sgn(bv)<0
|
||||
b2 = sgn(wv)<0
|
||||
b3 = sgn(lv)<0
|
||||
b4 = sgn(fv)<0
|
||||
txt.print_bool(b1)
|
||||
txt.print_bool(b2)
|
||||
txt.print_bool(b3)
|
||||
txt.print_bool(b4)
|
||||
txt.nl()
|
||||
|
||||
txt.print_ulhex(lv, true) ; $555b177b
|
||||
|
||||
|
||||
; 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()
|
||||
bv = 1
|
||||
wv = 1
|
||||
lv = 1
|
||||
fv = 1.1
|
||||
b1 = sgn(bv)<0
|
||||
b2 = sgn(wv)<0
|
||||
b3 = sgn(lv)<0
|
||||
b4 = sgn(fv)<0
|
||||
txt.print_bool(b1)
|
||||
txt.print_bool(b2)
|
||||
txt.print_bool(b3)
|
||||
txt.print_bool(b4)
|
||||
txt.nl()
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
sub start2() {
|
||||
uword uw
|
||||
word sw
|
||||
long lv
|
||||
float fl
|
||||
|
||||
|
||||
uw = 44555
|
||||
fl = uw as float
|
||||
txt.print_f(fl)
|
||||
txt.nl()
|
||||
fl /= 2
|
||||
uw = fl as uword
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
|
||||
sw = -8888
|
||||
fl = sw as float
|
||||
txt.print_f(fl)
|
||||
txt.nl()
|
||||
fl /= 2
|
||||
sw = fl as word
|
||||
txt.print_w(sw)
|
||||
txt.nl()
|
||||
|
||||
lv = -99886666
|
||||
fl = lv as float
|
||||
txt.print_f(fl)
|
||||
txt.nl()
|
||||
fl /= 2
|
||||
lv = fl as long
|
||||
txt.print_l(lv)
|
||||
txt.nl()
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -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-BETA7
|
||||
@@ -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)
|
||||
@@ -220,10 +220,12 @@ ffromub fpreg1, reg1 - fpreg1 = reg1 from usigned byte
|
||||
ffromsb fpreg1, reg1 - fpreg1 = reg1 from signed byte
|
||||
ffromuw fpreg1, reg1 - fpreg1 = reg1 from unsigned word
|
||||
ffromsw fpreg1, reg1 - fpreg1 = reg1 from signed word
|
||||
ffromsl fpreg1, reg1 - fpreg1 = reg1 from signed long
|
||||
ftoub reg1, fpreg1 - reg1 = fpreg1 as unsigned byte
|
||||
ftosb reg1, fpreg1 - reg1 = fpreg1 as signed byte
|
||||
ftouw reg1, fpreg1 - reg1 = fpreg1 as unsigned word
|
||||
ftosw reg1, fpreg1 - reg1 = fpreg1 as signed word
|
||||
ftosl reg1, fpreg1 - reg1 = fpreg1 as signed long
|
||||
fpow fpreg1, fpreg2 - fpreg1 = fpreg1 to the power of fpreg2
|
||||
fabs fpreg1, fpreg2 - fpreg1 = abs(fpreg2)
|
||||
fcomp reg1, fpreg1, fpreg2 - reg1 = result of comparison of fpreg1 and fpreg2: 0.b=equal, 1.b=fpreg1 is greater, -1.b=fpreg1 is smaller
|
||||
@@ -398,10 +400,12 @@ enum class Opcode {
|
||||
FFROMSB,
|
||||
FFROMUW,
|
||||
FFROMSW,
|
||||
FFROMSL,
|
||||
FTOUB,
|
||||
FTOSB,
|
||||
FTOUW,
|
||||
FTOSW,
|
||||
FTOSL,
|
||||
FPOW,
|
||||
FABS,
|
||||
FSIN,
|
||||
@@ -494,7 +498,8 @@ val OpcodesThatEndSSAblock = OpcodesThatBranchUnconditionally + arrayOf(
|
||||
val OpcodesThatSetStatusbitsIncludingCarry = arrayOf(
|
||||
Opcode.BIT,
|
||||
Opcode.CMP,
|
||||
Opcode.CMPI
|
||||
Opcode.CMPI,
|
||||
Opcode.SGN
|
||||
)
|
||||
val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
|
||||
Opcode.LOAD,
|
||||
@@ -787,10 +792,12 @@ val instructionFormats = mutableMapOf(
|
||||
Opcode.FFROMSB to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMUW to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMSW to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FFROMSL to InstructionFormat.from("F,>fr1,<r1"),
|
||||
Opcode.FTOUB to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOSB to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOUW to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOSW to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FTOSL to InstructionFormat.from("F,>r1,<fr1"),
|
||||
Opcode.FPOW to InstructionFormat.from("F,<>fr1,<fr2"),
|
||||
Opcode.FABS to InstructionFormat.from("F,>fr1,<fr2"),
|
||||
Opcode.FCOMP to InstructionFormat.from("F,>r1,<fr1,<fr2"),
|
||||
@@ -1086,10 +1093,21 @@ data class IRInstruction(
|
||||
private fun determineReg1Type(): IRDataType? {
|
||||
if(type==IRDataType.FLOAT) {
|
||||
// some float instructions have an integer (byte or word) register as well in reg1
|
||||
return if(opcode in arrayOf(Opcode.FFROMUB, Opcode.FFROMSB, Opcode.FTOUB, Opcode.FTOSB, Opcode.FCOMP, Opcode.LOADIX, Opcode.LOADX, Opcode.STOREX, Opcode.STOREIX, Opcode.STOREZX))
|
||||
IRDataType.BYTE
|
||||
else
|
||||
IRDataType.WORD
|
||||
return when (opcode) {
|
||||
Opcode.FFROMUB,
|
||||
Opcode.FFROMSB,
|
||||
Opcode.FTOUB,
|
||||
Opcode.FTOSB,
|
||||
Opcode.FCOMP,
|
||||
Opcode.LOADIX,
|
||||
Opcode.LOADX,
|
||||
Opcode.STOREX,
|
||||
Opcode.STOREIX,
|
||||
Opcode.STOREZX,
|
||||
Opcode.SGN -> IRDataType.BYTE
|
||||
Opcode.FFROMSL, Opcode.FTOSL -> IRDataType.LONG
|
||||
else -> IRDataType.WORD
|
||||
}
|
||||
}
|
||||
if(type==IRDataType.WORD) {
|
||||
// some word instructions have byte reg1
|
||||
@@ -1102,6 +1120,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>
|
||||
|
||||
|
||||
Loaded 100 of 106 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user