mirror of
https://github.com/irmen/prog8.git
synced 2026-10-23 12:02:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dbfb8ab76 | ||
|
|
e29ff1c848 | ||
|
|
585f6ffc9b | ||
|
|
46b94c17d6 | ||
|
|
7af8007447 | ||
|
|
16a2b2f566 | ||
|
|
ea2a90c3c5 | ||
|
|
5cda750e5e | ||
|
|
4e143d45c8 | ||
|
|
4c50980d81 | ||
|
|
2954f5f04d | ||
|
|
cac4c1eb1e | ||
|
|
0b1f30d98c | ||
|
|
c7b1e8d772 | ||
|
|
a4f7512d44 | ||
|
|
0d3ad80659 | ||
|
|
aba1a73e28 | ||
|
|
dca31b2ca3 | ||
|
|
0cb378ca31 | ||
|
|
cf551d2cc7 | ||
|
|
ac0c8a68f6 | ||
|
|
5986dcdd2f | ||
|
|
6be6eb2227 | ||
|
|
d34015eec5 | ||
|
|
255c5bfaca | ||
|
|
01c6754928 | ||
|
|
8eaf884f69 | ||
|
|
699a2bb7ab | ||
|
|
4a2dcd20d1 | ||
|
|
4e98fb75d6 | ||
|
|
64e66e732f | ||
|
|
7aec627f6b | ||
|
|
59a2fec176 | ||
|
|
edc5a5a94f | ||
|
|
c5b7edad82 | ||
|
|
124ffac4e4 | ||
|
|
6d2a36fb2b | ||
|
|
28b43b3e1d | ||
|
|
f7feaf158d | ||
|
|
2396f707c6 | ||
|
|
d4d8e1b1ba | ||
|
|
44fec2c729 | ||
|
|
a80a6913e3 | ||
|
|
0eac04c220 | ||
|
|
29dd758302 | ||
|
|
5c45adc7f0 | ||
|
|
ad22cf08cd | ||
|
|
2c2ae64194 | ||
|
|
97c2dadd16 | ||
|
|
b36e1e3baf | ||
|
|
2da35fec17 | ||
|
|
bdeac74cfc |
No files matched your search
+23
@@ -0,0 +1,23 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.framework.datatest" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-framework-datatest:5.9.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest/5.9.1/kotest-framework-datatest-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-datatest-jvm/5.9.1/kotest-framework-datatest-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.9.23/kotlin-stdlib-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.8.0/kotlinx-coroutines-core-jvm-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.9.1/kotest-common-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.9.23/kotlin-reflect-1.9.23.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.9.1/kotest-framework-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.9.1/kotest-assertions-shared-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.9.1/kotest-assertions-api-jvm-5.9.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.8.0/kotlinx-coroutines-jdk8-1.8.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.12/java-diff-utils-4.12.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.8.0/kotlinx-coroutines-test-jvm-1.8.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
</library>
|
||||
</component>
|
||||
@@ -29,6 +29,8 @@ How to get it/build it
|
||||
- Or, if you want/need a bleeding edge development version, you can:
|
||||
- download a build artifact zipfile from a recent [github action build](https://github.com/irmen/prog8/actions).
|
||||
- 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:
|
||||
- Arch (via AUR): [`prog8`](https://aur.archlinux.org/packages/prog8)
|
||||
|
||||
|
||||
@@ -3,7 +3,8 @@ package prog8.code
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayDeque
|
||||
|
||||
|
||||
class SymbolTableMaker(private val program: PtProgram, private val options: CompilationOptions) {
|
||||
fun make(): SymbolTable {
|
||||
@@ -13,8 +14,8 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
st.add(StNode(it.key, StNodeType.BUILTINFUNC, PtIdentifier(it.key, it.value.returnType ?: DataType.UNDEFINED, Position.DUMMY)))
|
||||
}
|
||||
|
||||
val scopestack = Stack<StNode>()
|
||||
scopestack.push(st)
|
||||
val scopestack = ArrayDeque<StNode>()
|
||||
scopestack.add(st)
|
||||
program.children.forEach {
|
||||
addToSt(it, scopestack)
|
||||
}
|
||||
@@ -35,7 +36,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
return st
|
||||
}
|
||||
|
||||
private fun addToSt(node: PtNode, scope: Stack<StNode>) {
|
||||
private fun addToSt(node: PtNode, scope: ArrayDeque<StNode>) {
|
||||
val stNode = when(node) {
|
||||
is PtAsmSub -> {
|
||||
val parameters = node.parameters.map { StRomSubParameter(it.first, it.second.type) }
|
||||
@@ -104,7 +105,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
val size = (node.args[1] as PtNumber).number.toUInt()
|
||||
val align = (node.args[2] as PtNumber).number.toUInt()
|
||||
// don't add memory slabs in nested scope, just put them in the top level of the ST
|
||||
scope.firstElement().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||
scope.first().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||
}
|
||||
null
|
||||
}
|
||||
@@ -112,14 +113,14 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
}
|
||||
|
||||
if(stNode!=null) {
|
||||
scope.peek().add(stNode)
|
||||
scope.push(stNode)
|
||||
scope.last().add(stNode)
|
||||
scope.add(stNode)
|
||||
}
|
||||
node.children.forEach {
|
||||
addToSt(it, scope)
|
||||
}
|
||||
if(stNode!=null)
|
||||
scope.pop()
|
||||
scope.removeLast()
|
||||
}
|
||||
|
||||
private fun makeInitialArray(value: PtArray): List<StArrayElement> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package prog8.code.ast
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.util.*
|
||||
import java.util.Objects
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.truncate
|
||||
|
||||
@@ -42,7 +42,18 @@ sealed class PtExpression(val type: DataType, position: Position) : PtNode(posit
|
||||
else
|
||||
other.left isSameAs left && other.right isSameAs right
|
||||
}
|
||||
is PtContainmentCheck -> other is PtContainmentCheck && other.type==type && other.element isSameAs element && other.iterable isSameAs iterable
|
||||
is PtContainmentCheck -> {
|
||||
if(other !is PtContainmentCheck || other.type != type || !(other.needle isSameAs needle))
|
||||
false
|
||||
else {
|
||||
if(haystackHeapVar!=null)
|
||||
other.haystackHeapVar!=null && other.haystackHeapVar!! isSameAs haystackHeapVar!!
|
||||
else if(haystackValues!=null)
|
||||
other.haystackValues!=null && other.haystackValues!! isSameAs haystackValues!!
|
||||
else
|
||||
false
|
||||
}
|
||||
}
|
||||
is PtIdentifier -> other is PtIdentifier && other.type==type && other.name==name
|
||||
is PtIrRegister -> other is PtIrRegister && other.type==type && other.register==register
|
||||
is PtMemoryByte -> other is PtMemoryByte && other.address isSameAs address
|
||||
@@ -195,10 +206,17 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
|
||||
|
||||
|
||||
class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, position) {
|
||||
val element: PtExpression
|
||||
val needle: PtExpression
|
||||
get() = children[0] as PtExpression
|
||||
val iterable: PtIdentifier
|
||||
get() = children[1] as PtIdentifier
|
||||
val haystackHeapVar: PtIdentifier?
|
||||
get() = children[1] as? PtIdentifier
|
||||
val haystackValues: PtArray?
|
||||
get() = children[1] as? PtArray
|
||||
|
||||
companion object {
|
||||
val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
|
||||
val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,12 +245,6 @@ class PtMemoryByte(position: Position) : PtExpression(DataType.UBYTE, position)
|
||||
|
||||
|
||||
class PtBool(val value: Boolean, position: Position) : PtExpression(DataType.BOOL, position) {
|
||||
|
||||
companion object {
|
||||
fun fromNumber(number: Number, position: Position): PtBool =
|
||||
PtBool(number != 0.0, position)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = Objects.hash(type, value)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
@@ -302,6 +314,7 @@ class PtRange(type: DataType, position: Position) : PtExpression(type, position)
|
||||
fun toConstantIntegerRange(): IntProgression? {
|
||||
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||
return when {
|
||||
fromVal == toVal -> fromVal .. toVal
|
||||
fromVal <= toVal -> when {
|
||||
stepVal <= 0 -> IntRange.EMPTY
|
||||
stepVal == 1 -> fromVal..toVal
|
||||
|
||||
@@ -5,6 +5,7 @@ import prog8.code.core.*
|
||||
|
||||
sealed interface IPtSubroutine {
|
||||
val name: String
|
||||
val scopedName: String
|
||||
}
|
||||
|
||||
class PtAsmSub(
|
||||
|
||||
@@ -27,7 +27,6 @@ class CompilationOptions(val output: OutputType,
|
||||
var slabsHighBank: Int? = null,
|
||||
var slabsGolden: Boolean = false,
|
||||
var splitWordArrays: Boolean = false,
|
||||
var strictBool: Boolean = true,
|
||||
var breakpointCpuInstruction: String? = null,
|
||||
var outputDir: Path = Path(""),
|
||||
var symbolDefs: Map<String, String> = emptyMap()
|
||||
|
||||
@@ -10,9 +10,8 @@ fun optimizeIntermediateAst(program: PtProgram, options: CompilationOptions, st:
|
||||
if (!options.optimize)
|
||||
return
|
||||
while (errors.noErrors() &&
|
||||
(optimizeCommonSubExpressions(program, errors)
|
||||
+ optimizeBitTest(program, options)
|
||||
+ optimizeAssignTargets(program, st, errors)) > 0
|
||||
(optimizeBitTest(program, options)
|
||||
+ optimizeAssignTargets(program, st, errors)) > 0
|
||||
) {
|
||||
// keep rolling
|
||||
}
|
||||
@@ -28,97 +27,6 @@ private fun walkAst(root: PtNode, act: (node: PtNode, depth: Int) -> Boolean) {
|
||||
}
|
||||
|
||||
|
||||
private var tempVarCounter = 0
|
||||
|
||||
private fun optimizeCommonSubExpressions(program: PtProgram, errors: IErrorReporter): Int {
|
||||
|
||||
fun extractableSubExpr(expr: PtExpression): Boolean {
|
||||
if(expr is PtArrayIndexer && expr.index.isSimple())
|
||||
return false
|
||||
if (expr is PtMemoryByte && expr.address.isSimple())
|
||||
return false
|
||||
|
||||
val result = if(expr is PtBinaryExpression)
|
||||
expr.type !in ByteDatatypes ||
|
||||
!(expr.left.isSimple() && expr.right.isSimple()) ||
|
||||
(expr.operator !in LogicalOperators && expr.operator !in BitwiseOperators)
|
||||
else if (expr is PtArrayIndexer && expr.type !in ByteDatatypes)
|
||||
true
|
||||
else
|
||||
!expr.isSimple()
|
||||
return result
|
||||
}
|
||||
|
||||
// for each Binaryexpression, recurse to find a common subexpression pair therein.
|
||||
val commons = mutableMapOf<PtBinaryExpression, Pair<PtExpression, PtExpression>>()
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
if(node is PtBinaryExpression) {
|
||||
val subExpressions = mutableListOf<PtExpression>()
|
||||
walkAst(node.left) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
walkAst(node.right) { subNode: PtNode, subDepth: Int ->
|
||||
if (subNode is PtExpression) {
|
||||
if(extractableSubExpr(subNode)) subExpressions.add(subNode)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
outer@for (first in subExpressions) {
|
||||
for (second in subExpressions) {
|
||||
if (first!==second && first isSameAs second) {
|
||||
commons[node] = first to second
|
||||
break@outer // do only 1 replacement at a time per binaryexpression
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
} else true
|
||||
}
|
||||
|
||||
// replace common subexpressions by a temp variable that is assigned only once.
|
||||
// TODO: check for commonalities across multiple separate expressions in the current scope, not only inside a single line
|
||||
commons.forEach { binexpr, (occurrence1, occurrence2) ->
|
||||
val (stmtContainer, stmt) = findContainingStatements(binexpr)
|
||||
val occurrence1idx = occurrence1.parent.children.indexOf(occurrence1)
|
||||
val occurrence2idx = occurrence2.parent.children.indexOf(occurrence2)
|
||||
val containerScopedName = findScopeName(stmtContainer)
|
||||
tempVarCounter++
|
||||
val tempvarName = "prog8_subexprvar_$tempVarCounter"
|
||||
// TODO: some tempvars could be reused, if they are from different lines
|
||||
|
||||
val datatype = occurrence1.type
|
||||
val singleReplacement1 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence1.position)
|
||||
val singleReplacement2 = PtIdentifier("$containerScopedName.$tempvarName", datatype, occurrence2.position)
|
||||
occurrence1.parent.children[occurrence1idx] = singleReplacement1
|
||||
singleReplacement1.parent = occurrence1.parent
|
||||
occurrence2.parent.children[occurrence2idx] = singleReplacement2
|
||||
singleReplacement2.parent = occurrence2.parent
|
||||
|
||||
val tempassign = PtAssignment(binexpr.position).also { assign ->
|
||||
assign.add(PtAssignTarget(false, binexpr.position).also { tgt->
|
||||
tgt.add(PtIdentifier("$containerScopedName.$tempvarName", datatype, binexpr.position))
|
||||
})
|
||||
assign.add(occurrence1)
|
||||
occurrence1.parent = assign
|
||||
}
|
||||
stmtContainer.children.add(stmtContainer.children.indexOf(stmt), tempassign)
|
||||
tempassign.parent = stmtContainer
|
||||
|
||||
val tempvar = PtVariable(tempvarName, datatype, ZeropageWish.NOT_IN_ZEROPAGE, null, null, binexpr.position)
|
||||
stmtContainer.add(0, tempvar)
|
||||
tempvar.parent = stmtContainer
|
||||
|
||||
// errors.info("common subexpressions replaced by a tempvar, maybe simplify the expression manually", binexpr.position)
|
||||
}
|
||||
|
||||
return commons.size
|
||||
}
|
||||
|
||||
|
||||
private fun optimizeAssignTargets(program: PtProgram, st: SymbolTable, errors: IErrorReporter): Int {
|
||||
var changes = 0
|
||||
walkAst(program) { node: PtNode, depth: Int ->
|
||||
@@ -235,24 +143,3 @@ internal fun isSame(identifier: PtIdentifier, type: DataType, returnedRegister:
|
||||
}
|
||||
return false // there are no identifiers directly corresponding to cpu registers
|
||||
}
|
||||
|
||||
|
||||
internal fun findScopeName(node: PtNode): String {
|
||||
var parent=node
|
||||
while(parent !is PtNamedNode)
|
||||
parent = parent.parent
|
||||
return parent.scopedName
|
||||
}
|
||||
|
||||
|
||||
internal fun findContainingStatements(node: PtNode): Pair<PtNode, PtNode> { // returns (parentstatementcontainer, childstatement)
|
||||
var parent = node.parent
|
||||
var child = node
|
||||
while(true) {
|
||||
if(parent is IPtStatementContainer) {
|
||||
return parent to child
|
||||
}
|
||||
child=parent
|
||||
parent=parent.parent
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ dependencies {
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:2.0.0"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
|
||||
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
}
|
||||
|
||||
@@ -14,5 +14,6 @@
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -9,7 +9,7 @@ import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayDeque
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.writeLines
|
||||
|
||||
@@ -232,7 +232,7 @@ class AsmGen6502Internal (
|
||||
private val functioncallAsmGen = FunctionCallAsmGen(program, this)
|
||||
private val programGen = ProgramAndVarsGen(program, options, errors, symbolTable, functioncallAsmGen, this, allocator, zeropage)
|
||||
private val anyExprGen = AnyExprAsmGen(this)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, options, anyExprGen, allocator)
|
||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, anyExprGen, allocator)
|
||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||
private val ifElseAsmgen = IfElseAsmGen(program, symbolTable, this, allocator, assignmentAsmGen, errors)
|
||||
|
||||
@@ -374,7 +374,7 @@ class AsmGen6502Internal (
|
||||
val name = asmSymbolName(identifier.name)
|
||||
|
||||
// see if we're inside a subroutine, if so, remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block)
|
||||
val subName = identifier.definingSub()?.scopedName
|
||||
val subName = identifier.definingISub()?.scopedName
|
||||
return if (subName != null && name.length>subName.length && name.startsWith(subName) && name[subName.length] == '.')
|
||||
name.drop(subName.length + 1)
|
||||
else
|
||||
@@ -385,14 +385,14 @@ class AsmGen6502Internal (
|
||||
val name = asmVariableName(identifier.name)
|
||||
|
||||
// see if we're inside a subroutine, if so, remove the whole prefix and just make the variable name locally scoped (64tass scopes it to the proper .proc block)
|
||||
val subName = identifier.definingSub()?.scopedName
|
||||
val subName = identifier.definingISub()?.scopedName
|
||||
return if (subName != null && name.length>subName.length && name.startsWith(subName) && name[subName.length] == '.')
|
||||
name.drop(subName.length+1)
|
||||
else
|
||||
name
|
||||
}
|
||||
|
||||
fun asmVariableName(st: StNode, scope: PtSub?): String {
|
||||
fun asmVariableName(st: StNode, scope: IPtSubroutine?): String {
|
||||
val name = asmVariableName(st.scopedName)
|
||||
if(scope==null)
|
||||
return name
|
||||
@@ -772,7 +772,7 @@ class AsmGen6502Internal (
|
||||
|
||||
private fun translate(stmt: PtRepeatLoop) {
|
||||
val endLabel = makeLabel("repeatend")
|
||||
loopEndLabels.push(endLabel)
|
||||
loopEndLabels.add(endLabel)
|
||||
|
||||
when (stmt.count) {
|
||||
is PtNumber -> {
|
||||
@@ -816,7 +816,7 @@ class AsmGen6502Internal (
|
||||
}
|
||||
}
|
||||
|
||||
loopEndLabels.pop()
|
||||
loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun repeatWordCount(iterations: Int, stmt: PtRepeatLoop) {
|
||||
|
||||
@@ -37,7 +37,7 @@ internal class ForLoopsAsmGen(
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
val modifiedLabel = asmgen.makeLabel("for_modified")
|
||||
val modifiedLabel2 = asmgen.makeLabel("for_modifiedb")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val stepsize=range.step.asConstInteger()!!
|
||||
|
||||
if(stepsize < -1) {
|
||||
@@ -48,25 +48,43 @@ internal class ForLoopsAsmGen(
|
||||
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
if (stepsize==1 || stepsize==-1) {
|
||||
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||
if (stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel""")
|
||||
}
|
||||
else if (stepsize==1 || stepsize==-1) {
|
||||
// bytes array, step 1 or -1
|
||||
|
||||
val incdec = if(stepsize==1) "inc" else "dec"
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt==DataType.ARRAY_B) {
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
if(stepsize<0)
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
clc
|
||||
sbc $varname
|
||||
bvc +
|
||||
eor #${'$'}80
|
||||
+ bpl $endLabel""")
|
||||
}
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
@@ -75,12 +93,13 @@ internal class ForLoopsAsmGen(
|
||||
eor #${'$'}80
|
||||
+ bmi $endLabel""")
|
||||
} else {
|
||||
if(stepsize<0)
|
||||
if(stepsize<0) {
|
||||
asmgen.out("""
|
||||
cmp $varname
|
||||
beq +
|
||||
bcs $endLabel
|
||||
+""")
|
||||
}
|
||||
else
|
||||
asmgen.out(" cmp $varname | bcc $endLabel")
|
||||
asmgen.out(" sta $modifiedLabel+1")
|
||||
@@ -100,8 +119,6 @@ $modifiedLabel cmp #0 ; modified
|
||||
// bytes, step >= 2 or <= -2
|
||||
|
||||
// loop over byte range via loopvar
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.assignExpressionToVariable(range.from, varname, ArrayToElementTypes.getValue(iterableDt))
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.A, false)
|
||||
// pre-check for end already reached
|
||||
if(iterableDt==DataType.ARRAY_B) {
|
||||
@@ -155,58 +172,85 @@ $modifiedLabel cmp #0 ; modified
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
when {
|
||||
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
if(stepsize==-1 && range.to.asConstInteger()==0) {
|
||||
// simple loop downto 0 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize==-1 && range.to.asConstInteger()==1) {
|
||||
// simple loop downto 1 step -1 (words)
|
||||
asmgen.out(loopLabel)
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #1
|
||||
bne +
|
||||
lda $varname+1
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize == 1 || stepsize == -1) {
|
||||
// words, step 1 or -1
|
||||
|
||||
stepsize == 1 || stepsize == -1 -> {
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.out("""
|
||||
lda $varname+1
|
||||
$modifiedLabel cmp #0 ; modified
|
||||
bne +
|
||||
lda $varname
|
||||
$modifiedLabel2 cmp #0 ; modified
|
||||
beq $endLabel""")
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
if(stepsize==1) {
|
||||
asmgen.out("""
|
||||
+ inc $varname
|
||||
bne $loopLabel
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
asmgen.jmp(loopLabel)
|
||||
} else {
|
||||
asmgen.out("""
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
asmgen.out(endLabel)
|
||||
asmgen.jmp(loopLabel)
|
||||
}
|
||||
stepsize > 0 -> {
|
||||
|
||||
// (u)words, step >= 2
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
asmgen.out(endLabel)
|
||||
}
|
||||
else if (stepsize > 0) {
|
||||
// (u)words, step >= 2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
if (iterableDt == DataType.ARRAY_UW) {
|
||||
asmgen.out("""
|
||||
if (iterableDt == DataType.ARRAY_UW) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -222,8 +266,8 @@ $modifiedLabel2 lda #0 ; modified
|
||||
bcc $endLabel
|
||||
bcs $loopLabel
|
||||
$endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
clc
|
||||
adc #<$stepsize
|
||||
@@ -239,22 +283,20 @@ $modifiedLabel lda #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
else {
|
||||
|
||||
// (u)words, step <= -2
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
assignLoopvarWord(stmt, range)
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
// (u)words, step <= -2
|
||||
asmgen.assignExpressionToRegister(range.to, RegisterOrPair.AY)
|
||||
precheckFromToWord(iterableDt, stepsize, varname, endLabel)
|
||||
asmgen.out("""
|
||||
sty $modifiedLabel+1
|
||||
sta $modifiedLabel2+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
asmgen.translate(stmt.statements)
|
||||
|
||||
asmgen.out("""
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
sec
|
||||
sbc #<${stepsize.absoluteValue}
|
||||
@@ -271,13 +313,12 @@ $modifiedLabel sbc #0 ; modified
|
||||
eor #$80
|
||||
+ bpl $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun precheckFromToWord(iterableDt: DataType, stepsize: Int, fromVar: String, endLabel: String) {
|
||||
@@ -333,7 +374,7 @@ $endLabel""")
|
||||
private fun translateForOverIterableVar(stmt: PtForLoop, iterableDt: DataType, ident: PtIdentifier) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val iterableName = asmgen.asmVariableName(ident)
|
||||
val numElements = when(val symbol = asmgen.symbolTable.lookup(ident.name)) {
|
||||
is StStaticVariable -> symbol.length!!
|
||||
@@ -477,17 +518,19 @@ $loopLabel sty $indexVar
|
||||
}
|
||||
else -> throw AssemblyError("can't iterate over $iterableDt")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun translateForOverConstRange(stmt: PtForLoop, iterableDt: DataType, range: IntProgression) {
|
||||
if (range.isEmpty() || range.step==0)
|
||||
throw AssemblyError("empty range or step 0")
|
||||
if(iterableDt==DataType.ARRAY_B || iterableDt==DataType.ARRAY_UB) {
|
||||
if(range.last==range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
||||
if(range.step==1 && range.last>range.first) return translateForSimpleByteRangeAsc(stmt, range)
|
||||
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range)
|
||||
if(range.step==-1 && range.last<range.first) return translateForSimpleByteRangeDesc(stmt, range, iterableDt==DataType.ARRAY_UB)
|
||||
}
|
||||
else if(iterableDt==DataType.ARRAY_W || iterableDt==DataType.ARRAY_UW) {
|
||||
if(range.last==range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
||||
if(range.step==1 && range.last>range.first) return translateForSimpleWordRangeAsc(stmt, range)
|
||||
if(range.step==-1 && range.last<range.first) return translateForSimpleWordRangeDesc(stmt, range)
|
||||
}
|
||||
@@ -495,7 +538,7 @@ $loopLabel sty $indexVar
|
||||
// not one of the easy cases, generate more complex code...
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
when(iterableDt) {
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
// loop over byte range via loopvar, step >= 2 or <= -2
|
||||
@@ -507,7 +550,7 @@ $loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
when (range.step) {
|
||||
0, 1, -1 -> {
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $stmt")
|
||||
throw AssemblyError("step 0, 1 and -1 should have been handled specifically $range ${stmt.position}")
|
||||
}
|
||||
2 -> {
|
||||
if(range.last==255 || range.last==254) {
|
||||
@@ -601,13 +644,13 @@ $loopLabel""")
|
||||
}
|
||||
else -> throw AssemblyError("range expression can only be byte or word")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeAsc(stmt: PtForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
@@ -627,50 +670,50 @@ $endLabel""")
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression) {
|
||||
private fun translateForSimpleByteRangeDesc(stmt: PtForLoop, range: IntProgression, unsigned: Boolean) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
lda #${range.first}
|
||||
sta $varname
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
when (range.last) {
|
||||
0 -> {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
beq $endLabel
|
||||
dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
if(!unsigned || range.first<=127) {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bpl $loopLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #255
|
||||
bne $loopLabel""")
|
||||
}
|
||||
}
|
||||
1 -> {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
bne $loopLabel""")
|
||||
}
|
||||
else -> {
|
||||
asmgen.out("""
|
||||
dec $varname
|
||||
lda $varname
|
||||
cmp #${range.last-1}
|
||||
bne $loopLabel
|
||||
$endLabel""")
|
||||
bne $loopLabel""")
|
||||
}
|
||||
}
|
||||
asmgen.loopEndLabels.pop()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeAsc(stmt: PtForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
@@ -691,13 +734,13 @@ $loopLabel""")
|
||||
inc $varname+1""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun translateForSimpleWordRangeDesc(stmt: PtForLoop, range: IntProgression) {
|
||||
val loopLabel = asmgen.makeLabel("for_loop")
|
||||
val endLabel = asmgen.makeLabel("for_end")
|
||||
asmgen.loopEndLabels.push(endLabel)
|
||||
asmgen.loopEndLabels.add(endLabel)
|
||||
val varname = asmgen.asmVariableName(stmt.variable)
|
||||
asmgen.out("""
|
||||
lda #<${range.first}
|
||||
@@ -706,20 +749,29 @@ $loopLabel""")
|
||||
sty $varname+1
|
||||
$loopLabel""")
|
||||
asmgen.translate(stmt.statements)
|
||||
if(range.last==0) {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
bne ++
|
||||
lda $varname+1
|
||||
beq $endLabel""")
|
||||
} else {
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
beq $endLabel""")
|
||||
}
|
||||
asmgen.out("""
|
||||
lda $varname
|
||||
cmp #<${range.last}
|
||||
bne +
|
||||
lda $varname+1
|
||||
cmp #>${range.last}
|
||||
beq $endLabel
|
||||
+ lda $varname
|
||||
bne +
|
||||
dec $varname+1
|
||||
+ dec $varname""")
|
||||
asmgen.jmp(loopLabel)
|
||||
asmgen.out(endLabel)
|
||||
asmgen.loopEndLabels.pop()
|
||||
asmgen.loopEndLabels.removeLast()
|
||||
}
|
||||
|
||||
private fun assignLoopvarWord(stmt: PtForLoop, range: PtRange) =
|
||||
|
||||
@@ -82,7 +82,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
is PtAddressOf -> false
|
||||
is PtIdentifier -> false
|
||||
is PtIrRegister -> false
|
||||
is PtMemoryByte -> true // TODO might not actually need extra registers if the value has to end up in A
|
||||
is PtMemoryByte -> arg.address !is PtNumber && arg.address !is PtIdentifier
|
||||
is PtNumber -> false
|
||||
is PtBool -> false
|
||||
else -> true
|
||||
@@ -205,7 +205,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
if(value is PtIdentifier) {
|
||||
val addr = PtAddressOf(Position.DUMMY)
|
||||
addr.add(value)
|
||||
addr.parent = sub as PtNode
|
||||
addr.parent = scope as PtNode
|
||||
AsmAssignSource.fromAstSource(addr, program, asmgen).adjustSignedUnsigned(target)
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
@@ -213,7 +213,7 @@ internal class FunctionCallAsmGen(private val program: PtProgram, private val as
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(value, program, asmgen).adjustSignedUnsigned(target)
|
||||
}
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, Position.DUMMY), scope)
|
||||
asmgen.translateNormalAssignment(AsmAssignment(src, target, program.memsizer, target.position), scope)
|
||||
}
|
||||
return RegisterOrStatusflag(register, null)
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@ import prog8.codegen.cpu6502.VariableAllocator
|
||||
import prog8.codegen.cpu6502.returnsWhatWhere
|
||||
|
||||
|
||||
internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val options: CompilationOptions,
|
||||
private val anyExprGen: AnyExprAsmGen,
|
||||
private val allocator: VariableAllocator) {
|
||||
internal class AssignmentAsmGen(
|
||||
private val program: PtProgram,
|
||||
private val asmgen: AsmGen6502Internal,
|
||||
private val anyExprGen: AnyExprAsmGen,
|
||||
private val allocator: VariableAllocator
|
||||
) {
|
||||
private val augmentableAsmGen = AugmentableAssignmentAsmGen(program, this, asmgen, allocator)
|
||||
|
||||
fun translate(assignment: PtAssignment) {
|
||||
@@ -47,6 +48,26 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
|
||||
asmgen.translate(values)
|
||||
|
||||
val assignmentTargets = assignment.children.dropLast(1)
|
||||
if(sub.returns.size==assignmentTargets.size) {
|
||||
// because we can only handle integer results right now we can just zip() it all up
|
||||
val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
||||
if (statusFlagResults.isEmpty())
|
||||
assignRegisterResults(registersResults)
|
||||
else if(registersResults.isEmpty())
|
||||
assignOnlyTheStatusFlagsResults(false, statusFlagResults)
|
||||
else
|
||||
assignStatusFlagsAndRegistersResults(statusFlagResults, registersResults)
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignStatusFlagsAndRegistersResults(
|
||||
statusFlagResults: List<Pair<StRomSubParameter, PtNode>>,
|
||||
registersResults: List<Pair<StRomSubParameter, PtNode>>
|
||||
) {
|
||||
|
||||
fun needsToSaveA(registersResults: List<Pair<StRomSubParameter, PtNode>>): Boolean =
|
||||
if(registersResults.isEmpty())
|
||||
false
|
||||
@@ -55,124 +76,123 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
else
|
||||
true
|
||||
|
||||
fun assignCarryFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
||||
if(saveA) asmgen.out(" pha")
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
if(registersResults.all {
|
||||
val tgt = it.second as PtAssignTarget
|
||||
tgt.void || tgt.identifier!=null})
|
||||
{
|
||||
// all other results are just stored into identifiers directly so first handle those
|
||||
// (simple store instructions that don't modify the carry flag)
|
||||
assignRegisterResults(registersResults)
|
||||
assignOnlyTheStatusFlagsResults(false, statusFlagResults)
|
||||
} else {
|
||||
val saveA = needsToSaveA(registersResults)
|
||||
assignOnlyTheStatusFlagsResults(saveA, statusFlagResults)
|
||||
assignRegisterResults(registersResults)
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignOnlyTheStatusFlagsResults(saveA: Boolean, statusFlagResults: List<Pair<StRomSubParameter, PtNode>>) {
|
||||
// assigning flags to their variables targets requires load-instructions that destroy flags
|
||||
// so if there's more than 1, we need to save and restore the flags
|
||||
val saveFlags = statusFlagResults.size>1
|
||||
|
||||
fun hasFlag(statusFlagResults: List<Pair<StRomSubParameter, PtNode>>, flag: Statusflag): PtAssignTarget? {
|
||||
for ((returns, target) in statusFlagResults) {
|
||||
if(returns.register.statusflag!! == flag)
|
||||
return target as PtAssignTarget
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun assignZeroFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
||||
if(saveA) asmgen.out(" pha")
|
||||
asmgen.out("""
|
||||
val targetCarry = hasFlag(statusFlagResults, Statusflag.Pc)
|
||||
val targetZero = hasFlag(statusFlagResults, Statusflag.Pz)
|
||||
val targetNeg = hasFlag(statusFlagResults, Statusflag.Pn)
|
||||
val targetOverflow = hasFlag(statusFlagResults, Statusflag.Pv)
|
||||
|
||||
if(saveA) asmgen.out(" pha")
|
||||
if(targetZero!=null && !targetZero.void)
|
||||
assignZeroFlagResult(targetZero, saveFlags)
|
||||
if(targetNeg!=null && !targetNeg.void)
|
||||
assignNegativeFlagResult(targetNeg, saveFlags)
|
||||
if(targetCarry!=null && !targetCarry.void)
|
||||
assignCarryFlagResult(targetCarry)
|
||||
if(targetOverflow!=null && !targetOverflow.void)
|
||||
assignOverflowFlagResult(targetOverflow)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
}
|
||||
|
||||
private fun assignRegisterResults(registersResults: List<Pair<StRomSubParameter, PtNode>>) {
|
||||
registersResults.forEach { (returns, target) ->
|
||||
target as PtAssignTarget
|
||||
if(!target.void) {
|
||||
val targetIdent = target.identifier
|
||||
val targetMem = target.memory
|
||||
if(targetIdent!=null || targetMem!=null) {
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
when(returns.type) {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(returns.register.registerOrPair in Cx16VirtualRegisters) {
|
||||
assignVirtualRegister(tgt, returns.register.registerOrPair!!)
|
||||
} else {
|
||||
assignRegisterByte(tgt, returns.register.registerOrPair!!.asCpuRegister(), false, false)
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignRegisterpairWord(tgt, returns.register.registerOrPair!!)
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assignCarryFlagResult(target: PtAssignTarget) {
|
||||
// overflow is not clobbered so no need to save/restore it
|
||||
asmgen.out(" lda #0 | rol a")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
}
|
||||
|
||||
private fun assignZeroFlagResult(target: PtAssignTarget, saveFlags: Boolean) {
|
||||
if(saveFlags) asmgen.out(" php")
|
||||
asmgen.out("""
|
||||
beq +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveFlags) asmgen.out(" plp")
|
||||
}
|
||||
|
||||
fun assignNegativeFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
||||
if(saveA) asmgen.out(" pha")
|
||||
asmgen.out("""
|
||||
private fun assignNegativeFlagResult(target: PtAssignTarget, saveFlags: Boolean) {
|
||||
if(saveFlags) asmgen.out(" php")
|
||||
asmgen.out("""
|
||||
bmi +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveFlags) asmgen.out(" plp")
|
||||
}
|
||||
|
||||
fun assignOverflowFlagResult(target: PtAssignTarget, saveA: Boolean) {
|
||||
if(saveA) asmgen.out(" pha")
|
||||
asmgen.out("""
|
||||
private fun assignOverflowFlagResult(target: PtAssignTarget) {
|
||||
// overflow is not clobbered so no need to save/restore it
|
||||
asmgen.out("""
|
||||
bvs +
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
if(saveA) asmgen.out(" pla")
|
||||
}
|
||||
|
||||
fun assignRegisterResults(registersResults: List<Pair<StRomSubParameter, PtNode>>) {
|
||||
registersResults.forEach { (returns, target) ->
|
||||
target as PtAssignTarget
|
||||
if(!target.void) {
|
||||
val targetIdent = target.identifier
|
||||
val targetMem = target.memory
|
||||
if(targetIdent!=null || targetMem!=null) {
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
when(returns.type) {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
if(returns.register.registerOrPair in Cx16VirtualRegisters) {
|
||||
assignVirtualRegister(tgt, returns.register.registerOrPair!!)
|
||||
} else {
|
||||
assignRegisterByte(tgt, returns.register.registerOrPair!!.asCpuRegister(), false, false)
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignRegisterpairWord(tgt, returns.register.registerOrPair!!)
|
||||
}
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
else TODO("array target for multi-value assignment") // Not done yet due to result register clobbering complexity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val assignmentTargets = assignment.children.dropLast(1)
|
||||
if(sub.returns.size==assignmentTargets.size) {
|
||||
// because we can only handle integer results right now we can just zip() it all up
|
||||
val (statusFlagResults, registersResults) = sub.returns.zip(assignmentTargets).partition { it.first.register.statusflag!=null }
|
||||
if(statusFlagResults.isNotEmpty()) {
|
||||
if(statusFlagResults.size>1)
|
||||
TODO("handle multiple status flag results")
|
||||
val (returns, target) = statusFlagResults.single()
|
||||
target as PtAssignTarget
|
||||
if(target.void) {
|
||||
// forget about the Carry status flag, only assign the normal return values
|
||||
assignRegisterResults(registersResults)
|
||||
return
|
||||
}
|
||||
if(registersResults.all {
|
||||
val tgt = it.second as PtAssignTarget
|
||||
tgt.void || tgt.identifier!=null})
|
||||
{
|
||||
// all other results are just stored into identifiers directly so first handle those
|
||||
// (simple store instructions that don't modify the carry flag)
|
||||
assignRegisterResults(registersResults)
|
||||
return when(returns.register.statusflag!!) {
|
||||
Statusflag.Pc -> assignCarryFlagResult(target, false)
|
||||
Statusflag.Pz -> assignZeroFlagResult(target, false)
|
||||
Statusflag.Pv -> assignOverflowFlagResult(target, false)
|
||||
Statusflag.Pn -> assignNegativeFlagResult(target, false)
|
||||
}
|
||||
}
|
||||
|
||||
val saveA = needsToSaveA(registersResults)
|
||||
when(returns.register.statusflag!!) {
|
||||
Statusflag.Pc -> assignCarryFlagResult(target, saveA)
|
||||
Statusflag.Pz -> assignZeroFlagResult(target, saveA)
|
||||
Statusflag.Pv -> assignOverflowFlagResult(target, saveA)
|
||||
Statusflag.Pn -> assignNegativeFlagResult(target, saveA)
|
||||
}
|
||||
}
|
||||
assignRegisterResults(registersResults)
|
||||
} else {
|
||||
throw AssemblyError("number of values and targets don't match")
|
||||
}
|
||||
val tgt = AsmAssignTarget.fromAstAssignment(target, target.definingISub(), asmgen)
|
||||
assignRegisterByte(tgt, CpuRegister.A, false, false)
|
||||
}
|
||||
|
||||
|
||||
fun translateNormalAssignment(assign: AsmAssignment, scope: IPtSubroutine?) {
|
||||
when(assign.source.kind) {
|
||||
SourceStorageKind.LITERALBOOLEAN -> {
|
||||
@@ -201,7 +221,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
val variable = assign.source.asmVarname
|
||||
when (assign.target.datatype) {
|
||||
DataType.BOOL -> {
|
||||
if (assign.source.datatype == DataType.BOOL || !options.strictBool) assignVariableByte(assign.target, variable)
|
||||
if (assign.source.datatype == DataType.BOOL) assignVariableByte(assign.target, variable)
|
||||
else throw AssemblyError("assigning non-bool variable to boolean, should have been typecasted")
|
||||
}
|
||||
DataType.UBYTE, DataType.BYTE -> assignVariableByte(assign.target, variable)
|
||||
@@ -335,7 +355,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
is PtNumber -> {
|
||||
val indexValue = index.number.toString()
|
||||
val indexValue = index.number.toInt().toString()
|
||||
asmgen.out("""
|
||||
lda $pointer
|
||||
sta P8ZP_SCRATCH_W2
|
||||
@@ -384,7 +404,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
return
|
||||
}
|
||||
is PtNumber -> {
|
||||
val indexValue = index.number.toString()
|
||||
val indexValue = index.number.toInt().toString()
|
||||
asmgen.out("""
|
||||
tax
|
||||
lda $pointer
|
||||
@@ -1483,11 +1503,16 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
// special optimization for bytevalue +/- pointervar[y] (actually: bytevalue +/- @(address) )
|
||||
val address = mem.address as? PtBinaryExpression
|
||||
if(address is PtBinaryExpression) {
|
||||
val constOffset = address.right.asConstInteger()
|
||||
// check that the offset is actually a byte (so that it fits in a single register)
|
||||
if(constOffset==null && address.right.type !in ByteDatatypes)
|
||||
return false
|
||||
if(constOffset!=null && constOffset !in -128..255)
|
||||
return false
|
||||
val ptrVar = address.left as? PtIdentifier
|
||||
if(ptrVar!=null && asmgen.isZpVar(ptrVar)) {
|
||||
assignExpressionToRegister(value, RegisterOrPair.A, false)
|
||||
val pointername = asmgen.asmVariableName(ptrVar)
|
||||
val constOffset = address.right.asConstInteger()
|
||||
if (constOffset != null && constOffset < 256) {
|
||||
// we have value + @(zpptr + 255), or value - @(zpptr+255)
|
||||
asmgen.out(" ldy #$constOffset")
|
||||
@@ -1805,9 +1830,56 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
|
||||
private fun containmentCheckIntoA(containment: PtContainmentCheck) {
|
||||
val elementDt = containment.element.type
|
||||
val symbol = asmgen.symbolTable.lookup(containment.iterable.name)!!
|
||||
val symbolName = asmgen.asmVariableName(symbol, containment.definingSub())
|
||||
val elementDt = containment.needle.type
|
||||
|
||||
if(containment.haystackValues!=null) {
|
||||
val haystack = containment.haystackValues!!.children.map {
|
||||
if(it is PtBool) it.asInt()
|
||||
else (it as PtNumber).number.toInt()
|
||||
}
|
||||
when(elementDt) {
|
||||
in ByteDatatypesWithBoolean -> {
|
||||
require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||
for(number in haystack) {
|
||||
asmgen.out("""
|
||||
cmp #$number
|
||||
beq +""")
|
||||
}
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
beq ++
|
||||
+ lda #1
|
||||
+""")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.AY, elementDt == DataType.WORD)
|
||||
val gottemLabel = asmgen.makeLabel("gottem")
|
||||
val endLabel = asmgen.makeLabel("end")
|
||||
for(number in haystack) {
|
||||
asmgen.out("""
|
||||
cmp #<$number
|
||||
bne +
|
||||
cpy #>$number
|
||||
beq $gottemLabel
|
||||
+ """)
|
||||
}
|
||||
asmgen.out("""
|
||||
lda #0
|
||||
beq $endLabel
|
||||
$gottemLabel lda #1
|
||||
$endLabel""")
|
||||
}
|
||||
DataType.FLOAT -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
|
||||
else -> throw AssemblyError("weird dt $elementDt")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
val symbol = asmgen.symbolTable.lookup(containment.haystackHeapVar!!.name)!!
|
||||
val symbolName = asmgen.asmVariableName(symbol, containment.definingISub())
|
||||
val (dt, numElements) = when(symbol) {
|
||||
is StStaticVariable -> symbol.dt to symbol.length!!
|
||||
is StMemVar -> symbol.dt to symbol.length!!
|
||||
@@ -1815,7 +1887,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
}
|
||||
when(dt) {
|
||||
DataType.STR -> {
|
||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, null, null)
|
||||
asmgen.out(" pla")
|
||||
@@ -1823,13 +1895,13 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
assignExpressionToRegister(containment.element, RegisterOrPair.FAC1, true)
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.FAC1, true)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr floats.containment_floatarray")
|
||||
}
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB -> {
|
||||
assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_BOOL -> {
|
||||
assignExpressionToRegister(containment.needle, RegisterOrPair.A, elementDt == DataType.BYTE)
|
||||
asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null)
|
||||
asmgen.out(" pla")
|
||||
@@ -1837,7 +1909,7 @@ internal class AssignmentAsmGen(private val program: PtProgram,
|
||||
asmgen.out(" jsr prog8_lib.containment_bytearray")
|
||||
}
|
||||
DataType.ARRAY_W, DataType.ARRAY_UW -> {
|
||||
assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt)
|
||||
assignExpressionToVariable(containment.needle, "P8ZP_SCRATCH_W1", elementDt)
|
||||
assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, null, null)
|
||||
asmgen.out(" ldy #$numElements")
|
||||
asmgen.out(" jsr prog8_lib.containment_wordarray")
|
||||
|
||||
@@ -225,7 +225,7 @@ internal class AugmentableAssignmentAsmGen(private val program: PtProgram,
|
||||
SourceStorageKind.LITERALNUMBER -> inplacemodificationSplitWordWithLiteralval(target.asmVarname, target.datatype, index, operator, value.number!!.number.toInt())
|
||||
else -> {
|
||||
// TODO: more optimized code for VARIABLE, REGISTER, MEMORY, ARRAY, EXPRESSION in the case of split-word arrays
|
||||
val scope = target.origAstTarget?.definingSub()
|
||||
val scope = target.origAstTarget?.definingISub()
|
||||
val regTarget = AsmAssignTarget.fromRegisters(RegisterOrPair.R0, false, target.position, scope, asmgen)
|
||||
val assignToReg = AsmAssignment(value, regTarget, program.memsizer, target.position)
|
||||
assignmentAsmGen.translateNormalAssignment(assignToReg, scope)
|
||||
|
||||
@@ -31,6 +31,7 @@ dependencies {
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
||||
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<orderEntry type="module" module-name="intermediate" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -57,10 +57,7 @@ internal class AssignmentGen(private val codeGen: IRCodeGen, private val express
|
||||
RegisterOrPair.XY -> IRInstruction(Opcode.LOADHXY, IRDataType.WORD, reg1=regNum)
|
||||
in Cx16VirtualRegisters -> IRInstruction(Opcode.LOADM, IRDataType.WORD, reg1=regNum, labelSymbol = "cx16.${returns.register.registerOrPair.toString().lowercase()}")
|
||||
null -> {
|
||||
when(returns.register.statusflag) {
|
||||
Statusflag.Pc -> IRInstruction(Opcode.LOADHA, IRDataType.BYTE, reg1=regNum)
|
||||
else -> throw AssemblyError("weird statusflag as returnvalue")
|
||||
}
|
||||
TODO("assign CPU status flag ${returns.register.statusflag!!}")
|
||||
}
|
||||
else -> throw AssemblyError("cannot load register")
|
||||
}
|
||||
|
||||
@@ -580,6 +580,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
val arr = (arg as? PtArrayIndexer)
|
||||
val index = arr?.index?.asConstInteger()
|
||||
if(arr!=null && index!=null) {
|
||||
if(arr.splitWords) TODO("IR rol/ror on split words array")
|
||||
val variable = arr.variable.name
|
||||
val itemsize = codeGen.program.memsizer.memorySize(arr.type)
|
||||
addInstr(result, IRInstruction(opcodeMemAndReg.first, vmDt, labelSymbol = variable, symbolOffset = index*itemsize), null)
|
||||
|
||||
@@ -168,55 +168,93 @@ internal class ExpressionGen(private val codeGen: IRCodeGen) {
|
||||
}
|
||||
|
||||
private fun translate(check: PtContainmentCheck): ExpressionCodeResult {
|
||||
val elementDt = check.needle.type
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
when(check.iterable.type) {
|
||||
|
||||
if(check.haystackValues!=null) {
|
||||
val haystack = check.haystackValues!!.children.map {
|
||||
if(it is PtBool) it.asInt()
|
||||
else (it as PtNumber).number.toInt()
|
||||
}
|
||||
when(elementDt) {
|
||||
in IntegerDatatypesWithBoolean -> {
|
||||
if (elementDt in ByteDatatypesWithBoolean) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE)
|
||||
if (elementDt in WordDatatypes) require(haystack.size in 0..PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD)
|
||||
val gottemLabel = codeGen.createLabelName()
|
||||
val endLabel = codeGen.createLabelName()
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
for(value in haystack){
|
||||
it += IRInstruction(Opcode.CMPI, irType(elementDt), elementTr.resultReg, immediate = value)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = gottemLabel)
|
||||
}
|
||||
it += IRInstruction(Opcode.LOAD, IRDataType.BYTE, elementTr.resultReg, immediate = 0)
|
||||
it += IRInstruction(Opcode.JUMP, labelSymbol = endLabel)
|
||||
}
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, elementTr.resultReg, immediate = 1), gottemLabel)
|
||||
result += IRCodeChunk(endLabel, null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, -1, -1)
|
||||
|
||||
}
|
||||
DataType.FLOAT -> throw AssemblyError("containmentchecks for floats should always be done on an array variable with subroutine")
|
||||
else -> throw AssemblyError("weird dt $elementDt")
|
||||
}
|
||||
}
|
||||
|
||||
val haystackVar = check.haystackHeapVar!!
|
||||
when(haystackVar.type) {
|
||||
DataType.STR -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 2), null)
|
||||
val elementTr = translateExpression(check.element)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
result += codeGen.makeSyscall(IMSyscall.STRING_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg), IRDataType.BYTE to elementTr.resultReg)
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.element)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
val iterableLength = codeGen.symbolTable.getLength(check.iterable.name)
|
||||
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.BYTEARRAY_CONTAINS, listOf(IRDataType.BYTE to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.element)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, elementTr.resultReg, -1)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
val iterableLength = codeGen.symbolTable.getLength(check.iterable.name)
|
||||
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.WORDARRAY_CONTAINS, listOf(IRDataType.WORD to elementTr.resultReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to elementTr.resultReg)
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=elementTr.resultReg, immediate = 0), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, elementTr.resultReg, -1)
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
addInstr(result, IRInstruction(Opcode.PREPARECALL, immediate = 3), null)
|
||||
val elementTr = translateExpression(check.element)
|
||||
val elementTr = translateExpression(check.needle)
|
||||
addToResult(result, elementTr, -1, elementTr.resultFpReg)
|
||||
val iterableTr = translateExpression(check.iterable)
|
||||
val iterableTr = translateExpression(haystackVar)
|
||||
addToResult(result, iterableTr, iterableTr.resultReg, -1)
|
||||
val lengthReg = codeGen.registers.nextFree()
|
||||
val resultReg = codeGen.registers.nextFree()
|
||||
val iterableLength = codeGen.symbolTable.getLength(check.iterable.name)
|
||||
val iterableLength = codeGen.symbolTable.getLength(haystackVar.name)
|
||||
addInstr(result, IRInstruction(Opcode.LOAD, IRDataType.BYTE, reg1=lengthReg, immediate = iterableLength!!), null)
|
||||
result += codeGen.makeSyscall(IMSyscall.FLOATARRAY_CONTAINS, listOf(IRDataType.FLOAT to elementTr.resultFpReg, IRDataType.WORD to iterableTr.resultReg, IRDataType.BYTE to lengthReg), IRDataType.BYTE to resultReg)
|
||||
addInstr(result, IRInstruction(Opcode.CMPI, IRDataType.BYTE, reg1=resultReg, immediate = 0), null)
|
||||
return ExpressionCodeResult(result, IRDataType.BYTE, resultReg, -1)
|
||||
}
|
||||
else -> throw AssemblyError("weird iterable dt ${check.iterable.type} for ${check.iterable.name}")
|
||||
else -> throw AssemblyError("weird iterable dt ${haystackVar.type} for ${haystackVar.name}")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -487,49 +487,76 @@ class IRCodeGen(
|
||||
val loopLabel = createLabelName()
|
||||
val result = mutableListOf<IRCodeChunkBase>()
|
||||
|
||||
val toTr = expressionEval.translateExpression(iterable.to)
|
||||
addToResult(result, toTr, toTr.resultReg, -1)
|
||||
val fromTr = expressionEval.translateExpression(iterable.from)
|
||||
addToResult(result, fromTr, fromTr.resultReg, -1)
|
||||
|
||||
val labelAfterFor = createLabelName()
|
||||
val precheckInstruction = if(loopvarDt in SignedDatatypes) {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGTSR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
|
||||
else
|
||||
IRInstruction(Opcode.BGTSR, loopvarDtIr, toTr.resultReg, fromTr.resultReg, labelSymbol=labelAfterFor)
|
||||
} else {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGTR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
|
||||
else
|
||||
IRInstruction(Opcode.BGTR, loopvarDtIr, toTr.resultReg, fromTr.resultReg, labelSymbol=labelAfterFor)
|
||||
}
|
||||
addInstr(result, precheckInstruction, null)
|
||||
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
if(step==1 || step==-1) {
|
||||
// if endvalue == loopvar, stop loop, else iterate
|
||||
if(loopvarDtIr==IRDataType.BYTE && step==-1 && iterable.to.asConstInteger()==0) {
|
||||
// downto 0 optimization (byte)
|
||||
val fromTr = expressionEval.translateExpression(iterable.from)
|
||||
addToResult(result, fromTr, fromTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.CMP, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = labelAfterFor)
|
||||
it += IRInstruction(Opcode.CMPI, loopvarDtIr, reg1 = fromTr.resultReg, immediate = 255)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
}
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = loopLabel), null)
|
||||
} else {
|
||||
// ind/dec index, then:
|
||||
// ascending: if endvalue >= loopvar, iterate
|
||||
// descending: if loopvar >= endvalue, iterate
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol = loopvarSymbol), null)
|
||||
if(step > 0)
|
||||
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = toTr.resultReg, fromTr.resultReg), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = fromTr.resultReg, toTr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.BSTPOS, labelSymbol = loopLabel), null)
|
||||
}
|
||||
result += IRCodeChunk(labelAfterFor, null)
|
||||
else if(step==-1 && iterable.to.asConstInteger()==1) {
|
||||
// downto 1 optimization (byte and word)
|
||||
val fromTr = expressionEval.translateExpression(iterable.from)
|
||||
addToResult(result, fromTr, fromTr.resultReg, -1)
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, -1)
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
}
|
||||
}
|
||||
else {
|
||||
val toTr = expressionEval.translateExpression(iterable.to)
|
||||
addToResult(result, toTr, toTr.resultReg, -1)
|
||||
val fromTr = expressionEval.translateExpression(iterable.from)
|
||||
addToResult(result, fromTr, fromTr.resultReg, -1)
|
||||
|
||||
val labelAfterFor = createLabelName()
|
||||
val precheckInstruction = if(loopvarDt in SignedDatatypes) {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGTSR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
|
||||
else
|
||||
IRInstruction(Opcode.BGTSR, loopvarDtIr, toTr.resultReg, fromTr.resultReg, labelSymbol=labelAfterFor)
|
||||
} else {
|
||||
if(step>0)
|
||||
IRInstruction(Opcode.BGTR, loopvarDtIr, fromTr.resultReg, toTr.resultReg, labelSymbol=labelAfterFor)
|
||||
else
|
||||
IRInstruction(Opcode.BGTR, loopvarDtIr, toTr.resultReg, fromTr.resultReg, labelSymbol=labelAfterFor)
|
||||
}
|
||||
addInstr(result, precheckInstruction, null)
|
||||
|
||||
addInstr(result, IRInstruction(Opcode.STOREM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol=loopvarSymbol), null)
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
if(step==1 || step==-1) {
|
||||
// if endvalue == loopvar, stop loop, else iterate
|
||||
result += IRCodeChunk(null, null).also {
|
||||
it += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = fromTr.resultReg, labelSymbol = loopvarSymbol)
|
||||
it += IRInstruction(Opcode.CMP, loopvarDtIr, reg1=toTr.resultReg, reg2=fromTr.resultReg)
|
||||
it += IRInstruction(Opcode.BSTEQ, labelSymbol = labelAfterFor)
|
||||
}
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
addInstr(result, IRInstruction(Opcode.JUMP, labelSymbol = loopLabel), null)
|
||||
} else {
|
||||
// ind/dec index, then:
|
||||
// ascending: if endvalue >= loopvar, iterate
|
||||
// descending: if loopvar >= endvalue, iterate
|
||||
result += addConstMem(loopvarDtIr, null, loopvarSymbol, step)
|
||||
addInstr(result, IRInstruction(Opcode.LOADM, loopvarDtIr, reg1=fromTr.resultReg, labelSymbol = loopvarSymbol), null)
|
||||
if(step > 0)
|
||||
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = toTr.resultReg, fromTr.resultReg), null)
|
||||
else
|
||||
addInstr(result, IRInstruction(Opcode.CMP, loopvarDtIr, reg1 = fromTr.resultReg, toTr.resultReg), null)
|
||||
addInstr(result, IRInstruction(Opcode.BSTPOS, labelSymbol = loopLabel), null)
|
||||
}
|
||||
result += IRCodeChunk(labelAfterFor, null)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -558,9 +585,25 @@ class IRCodeGen(
|
||||
result += chunk
|
||||
result += labelFirstChunk(translateNode(forLoop.statements), loopLabel)
|
||||
val chunk2 = addConstMem(loopvarDtIr, null, loopvarSymbol, iterable.step)
|
||||
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
chunk2 += IRInstruction(Opcode.CMPI, loopvarDtIr, reg1 = indexReg, immediate = rangeEndExclusiveWrapped)
|
||||
chunk2 += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
if(loopvarDtIr==IRDataType.BYTE && iterable.step==-1 && iterable.last==0) {
|
||||
// downto 0 optimization (byte)
|
||||
if(loopvarDt==DataType.BYTE || iterable.first<=127) {
|
||||
chunk2 += IRInstruction(Opcode.BSTPOS, labelSymbol = loopLabel)
|
||||
} else {
|
||||
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
chunk2 += IRInstruction(Opcode.CMPI, loopvarDtIr, reg1 = indexReg, immediate = rangeEndExclusiveWrapped)
|
||||
chunk2 += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
}
|
||||
}
|
||||
else if(iterable.step==-1 && iterable.last==1) {
|
||||
// downto 1 optimization (byte and word)
|
||||
chunk2 += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
} else {
|
||||
// downto some other value
|
||||
chunk2 += IRInstruction(Opcode.LOADM, loopvarDtIr, reg1 = indexReg, labelSymbol = loopvarSymbol)
|
||||
chunk2 += IRInstruction(Opcode.CMPI, loopvarDtIr, reg1 = indexReg, immediate = rangeEndExclusiveWrapped)
|
||||
chunk2 += IRInstruction(Opcode.BSTNE, labelSymbol = loopLabel)
|
||||
}
|
||||
result += chunk2
|
||||
return result
|
||||
}
|
||||
@@ -1648,7 +1691,7 @@ class IRCodeGen(
|
||||
|
||||
private fun translate(parameters: List<PtSubroutineParameter>) =
|
||||
parameters.map {
|
||||
val flattenedName = it.definingSub()!!.name + "." + it.name
|
||||
val flattenedName = it.definingISub()!!.name + "." + it.name
|
||||
if(symbolTable.lookup(flattenedName)==null)
|
||||
TODO("fix missing lookup for: $flattenedName parameter")
|
||||
val orig = symbolTable.lookup(flattenedName) as StStaticVariable
|
||||
|
||||
@@ -66,7 +66,7 @@ class ConstExprEvaluator {
|
||||
// DataType.WORD -> result = result.toShort().toInt()
|
||||
// else -> { /* keep as it is */ }
|
||||
// }
|
||||
return NumericLiteral.optimalNumeric(result.toDouble(), left.position)
|
||||
return NumericLiteral.optimalNumeric(left.type, null, result.toDouble(), left.position)
|
||||
}
|
||||
|
||||
private fun bitwiseXor(left: NumericLiteral, right: NumericLiteral): NumericLiteral {
|
||||
@@ -133,7 +133,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot add $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() + right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() + right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() + right.number, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@@ -150,7 +150,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot subtract $left and $right"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() - right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() - right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() - right.number, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@@ -167,7 +167,7 @@ class ConstExprEvaluator {
|
||||
val error = "cannot multiply ${left.type} and ${right.type}"
|
||||
return when (left.type) {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.number.toInt() * right.number.toInt(), left.position)
|
||||
in IntegerDatatypes -> NumericLiteral.optimalInteger(left.type, right.type, left.number.toInt() * right.number.toInt(), left.position)
|
||||
DataType.FLOAT -> NumericLiteral(DataType.FLOAT, left.number.toInt() * right.number, left.position)
|
||||
else -> throw ExpressionError(error, left.position)
|
||||
}
|
||||
@@ -190,7 +190,7 @@ class ConstExprEvaluator {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
val result: Int = left.number.toInt() / right.number.toInt()
|
||||
NumericLiteral.optimalInteger(result, left.position)
|
||||
NumericLiteral.optimalInteger(left.type, right.type, result, left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number==0.0) divideByZeroError(right.position)
|
||||
@@ -219,7 +219,7 @@ class ConstExprEvaluator {
|
||||
in IntegerDatatypes -> when (right.type) {
|
||||
in IntegerDatatypes -> {
|
||||
if(right.number.toInt()==0) divideByZeroError(right.position)
|
||||
NumericLiteral.optimalNumeric(left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
|
||||
NumericLiteral.optimalNumeric(left.type, right.type, left.number.toInt().toDouble() % right.number.toInt().toDouble(), left.position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(right.number ==0.0) divideByZeroError(right.position)
|
||||
|
||||
@@ -33,6 +33,14 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
}
|
||||
|
||||
override fun after(numLiteral: NumericLiteral, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(numLiteral.type==DataType.LONG) {
|
||||
// see if LONG values may be reduced to something smaller
|
||||
val smaller = NumericLiteral.optimalInteger(numLiteral.number.toInt(), numLiteral.position)
|
||||
if(smaller.type!=DataType.LONG)
|
||||
return listOf(IAstModification.ReplaceNode(numLiteral, smaller, parent))
|
||||
}
|
||||
|
||||
if(parent is Assignment) {
|
||||
val iDt = parent.target.inferType(program)
|
||||
if(iDt.isKnown && !iDt.isBool && !iDt.istype(numLiteral.type)) {
|
||||
@@ -164,13 +172,13 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
if(leftconst==null && rightconst!=null && rightconst.number<0.0) {
|
||||
if (expr.operator == "-") {
|
||||
// X - -1 ---> X + 1
|
||||
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
|
||||
val posNumber = NumericLiteral.optimalNumeric(rightconst.type, null, -rightconst.number, rightconst.position)
|
||||
val plusExpr = BinaryExpression(expr.left, "+", posNumber, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
|
||||
}
|
||||
else if (expr.operator == "+") {
|
||||
// X + -1 ---> X - 1
|
||||
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
|
||||
val posNumber = NumericLiteral.optimalNumeric(rightconst.type, null, -rightconst.number, rightconst.position)
|
||||
val plusExpr = BinaryExpression(expr.left, "-", posNumber, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
|
||||
}
|
||||
@@ -384,12 +392,13 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
||||
val numval = decl.value as? NumericLiteral
|
||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||
val valueDt = numval.inferType(program)
|
||||
if(valueDt istype DataType.LONG) {
|
||||
return noModifications // this is handled in the numericalvalue case
|
||||
}
|
||||
if(valueDt isnot decl.datatype) {
|
||||
if(decl.datatype!=DataType.BOOL || valueDt.isnot(DataType.UBYTE)) {
|
||||
val cast = numval.cast(decl.datatype, true)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
}
|
||||
val cast = numval.cast(decl.datatype, true)
|
||||
if (cast.isValid)
|
||||
return listOf(IAstModification.ReplaceNode(numval, cast.valueOrZero(), decl))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
|
||||
@@ -490,8 +490,7 @@ internal class ConstantIdentifierReplacer(
|
||||
if(numericLv!=null) {
|
||||
// arraysize initializer is a single value, and we know the array size.
|
||||
if(numericLv.type!=DataType.BOOL) {
|
||||
if(options.strictBool || numericLv.type !in ByteDatatypes)
|
||||
errors.err("initializer value is not a boolean", numericLv.position)
|
||||
errors.err("initializer value is not a boolean", numericLv.position)
|
||||
return null
|
||||
}
|
||||
val array = Array(size) {numericLv.number}.map { NumericLiteral(DataType.BOOL, it, numericLv.position) }.toTypedArray<Expression>()
|
||||
|
||||
@@ -11,9 +11,9 @@ import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.pow
|
||||
|
||||
class ExpressionSimplifier(private val program: Program, private val options: CompilationOptions, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
class ExpressionSimplifier(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
|
||||
@@ -246,20 +246,15 @@ class ExpressionSimplifier(private val program: Program, private val options: Co
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if (options.strictBool) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
} else
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if (options.strictBool) {
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
if (rightDt != leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,21 +269,16 @@ class ExpressionSimplifier(private val program: Program, private val options: Co
|
||||
}
|
||||
}
|
||||
if (rightVal?.number == 1.0) {
|
||||
if(options.strictBool) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
else if (rightVal?.number == 0.0) {
|
||||
if(options.strictBool) {
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
} else
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.left, parent))
|
||||
if(rightDt!=leftDt) {
|
||||
val right = NumericLiteral(leftDt, rightVal.number, rightVal.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, right, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,8 +59,8 @@ fun Program.inlineSubroutines(options: CompilationOptions): Int {
|
||||
return inliner.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter, options: CompilationOptions) : Int {
|
||||
val opti = ExpressionSimplifier(this, options, errors)
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
|
||||
val opti = ExpressionSimplifier(this, errors)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
@@ -200,35 +200,6 @@ class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
val loopvarDt = forLoop.loopVarDt(program)
|
||||
if(loopvarDt.istype(DataType.UWORD) || loopvarDt.istype(DataType.UBYTE)) {
|
||||
|
||||
fun incOrDec(inc: Boolean): Assignment {
|
||||
val pos = forLoop.position
|
||||
val loopVar = forLoop.loopVar
|
||||
val addSubOne = BinaryExpression(loopVar.copy(), if(inc) "+" else "-", NumericLiteral.optimalInteger(1, pos), pos, false)
|
||||
return Assignment(AssignTarget(loopVar.copy(), null, null, null, false, pos), addSubOne, AssignmentOrigin.USERCODE, pos)
|
||||
}
|
||||
|
||||
if (range != null && range.to.constValue(program)?.number == 0.0 && range.step.constValue(program)?.number==-1.0) {
|
||||
val fromExpr = range.from
|
||||
if(fromExpr.constValue(program)==null) {
|
||||
// FOR X = something DOWNTO 0 {...} --> X=something, DO { ... , X-- } UNTIL X=255 (or 65535 if uword)
|
||||
val pos = forLoop.position
|
||||
val checkValue = NumericLiteral(loopvarDt.getOr(DataType.UNDEFINED), if(loopvarDt.istype(DataType.UBYTE)) 255.0 else 65535.0, pos)
|
||||
val condition = BinaryExpression(forLoop.loopVar.copy(), "==", checkValue, pos)
|
||||
val decOne = incOrDec(false)
|
||||
forLoop.body.statements.add(decOne)
|
||||
val replacement = AnonymousScope(mutableListOf(
|
||||
Assignment(AssignTarget(forLoop.loopVar.copy(), null, null, null, false, pos),
|
||||
fromExpr, AssignmentOrigin.OPTIMIZER, pos),
|
||||
UntilLoop(forLoop.body, condition, pos)
|
||||
), pos)
|
||||
return listOf(IAstModification.ReplaceNode(forLoop, replacement, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ dependencies {
|
||||
testImplementation project(':codeCore')
|
||||
testImplementation project(':intermediate')
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.9.1'
|
||||
testImplementation 'io.kotest:kotest-framework-datatest:5.9.1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.assertions.core.jvm" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.runner.junit5.jvm" level="project" />
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
<orderEntry type="library" name="io.kotest.framework.datatest" level="project" />
|
||||
<orderEntry type="module" module-name="codeCore" />
|
||||
<orderEntry type="module" module-name="compilerAst" />
|
||||
<orderEntry type="module" module-name="codeOptimizers" />
|
||||
@@ -24,5 +24,6 @@
|
||||
<orderEntry type="module" module-name="codeGenIntermediate" />
|
||||
<orderEntry type="module" module-name="virtualmachine" />
|
||||
<orderEntry type="module" module-name="intermediate" scope="TEST" />
|
||||
<orderEntry type="library" name="antlr.antlr4" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -36,7 +36,7 @@ romsub $b1bf = AYINT() clobbers(A,X,Y) ; fac1-> signed word in 100-101
|
||||
; there is also floats.GIVUAYFAY - unsigned word in A/Y (lo/hi) to fac1
|
||||
; there is also floats.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also floats.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also floats.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||
; there is also floats.FREAD{S,U}24AXY that read (un)signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||
romsub $b391 = GIVAYF(ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y)
|
||||
|
||||
romsub $b3a2 = FREADUY(ubyte value @ Y) clobbers(A,X,Y) ; 8 bit unsigned Y -> float in fac1
|
||||
@@ -104,7 +104,6 @@ asmsub FREADUS32 () clobbers(A,X,Y) {
|
||||
|
||||
asmsub FREADS24AXY (ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A,X,Y) {
|
||||
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
|
||||
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
|
||||
%asm {{
|
||||
sty $62
|
||||
stx $63
|
||||
@@ -119,6 +118,39 @@ asmsub FREADS24AXY (ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A,X,Y)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADU24AXY(ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A, X, Y) -> float @FAC1 {
|
||||
; ---- fac1 = unsigned int24 (A/X/Y contain lo/mid/hi bytes)
|
||||
%asm{{
|
||||
FAC = $61
|
||||
sty FAC+1
|
||||
stx FAC+2
|
||||
sta FAC+3
|
||||
|
||||
cpy #$00
|
||||
bne +
|
||||
cpx #$00
|
||||
bne +
|
||||
cmp #$00
|
||||
beq ++
|
||||
|
||||
+ ldx #$98
|
||||
bit FAC+1
|
||||
bmi +
|
||||
|
||||
- dex
|
||||
asl FAC+3
|
||||
rol FAC+2
|
||||
rol FAC+1
|
||||
bpl -
|
||||
|
||||
+ stx FAC
|
||||
lda #$00
|
||||
sta FAC+4
|
||||
sta FAC+5
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
@@ -177,6 +209,13 @@ asmsub normalize(float value @FAC1) -> float @ FAC1 {
|
||||
}}
|
||||
}
|
||||
|
||||
; get the jiffy clock as a float
|
||||
asmsub time() -> float @ FAC1 {
|
||||
%asm {{
|
||||
jsr cbm.RDTIM
|
||||
jmp floats.FREADU24AXY
|
||||
}}
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
@@ -288,17 +288,30 @@ hline_zero2
|
||||
|
||||
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
|
||||
; Midpoint algorithm, filled
|
||||
; Midpoint algorithm, filled.
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word decisionOver2 = (1 as word)-radius
|
||||
ubyte last_y3 = ycenter+radius
|
||||
ubyte last_y4 = ycenter-radius
|
||||
ubyte new_y3, new_y4
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*2+1)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*2+1)
|
||||
|
||||
new_y3 = ycenter+radius
|
||||
if new_y3 != last_y3 {
|
||||
horizontal_line(xcenter-yy, last_y3, yy*2+1)
|
||||
last_y3 = new_y3
|
||||
}
|
||||
new_y4 = ycenter-radius
|
||||
if new_y4 != last_y4 {
|
||||
horizontal_line(xcenter-yy, last_y4, yy*2+1)
|
||||
last_y4 = new_y4
|
||||
}
|
||||
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
@@ -307,6 +320,10 @@ hline_zero2
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
; draw the final two spans
|
||||
yy--
|
||||
horizontal_line(xcenter-yy, last_y3, yy*2+1)
|
||||
horizontal_line(xcenter-yy, last_y4, yy*2+1)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -935,15 +935,14 @@ io_error:
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
@@ -62,4 +62,25 @@ emudbg {
|
||||
if is_emulator()
|
||||
EMU_DBGOUT2 = value
|
||||
}
|
||||
|
||||
sub reset_cpu_cycles() {
|
||||
if is_emulator()
|
||||
EMU_CPUCLK_L = 0
|
||||
}
|
||||
|
||||
asmsub cpu_cycles() -> uword @R0, uword @R1 {
|
||||
; -- returns the 32 bits cpu clock counter in R1:R0,
|
||||
; returns lower 16 bits in R0, upper 16 bits in R1.
|
||||
%asm {{
|
||||
lda p8v_EMU_CPUCLK_L
|
||||
sta cx16.r0L
|
||||
lda p8v_EMU_CPUCLK_M
|
||||
sta cx16.r0H
|
||||
lda p8v_EMU_CPUCLK_H
|
||||
sta cx16.r1L
|
||||
lda p8v_EMU_CPUCLK_U
|
||||
sta cx16.r1H
|
||||
rts
|
||||
}}
|
||||
}
|
||||
}
|
||||
@@ -90,6 +90,38 @@ asmsub FREADSA (byte value @A) clobbers(A,X,Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADU24AXY(ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A, X, Y) -> float @FAC1 {
|
||||
; ---- fac1 = unsigned int24 (A/X/Y contain lo/mid/hi bytes)
|
||||
%asm{{
|
||||
FAC = $C3
|
||||
sty FAC+1
|
||||
stx FAC+2
|
||||
sta FAC+3
|
||||
|
||||
cpy #$00
|
||||
bne +
|
||||
cpx #$00
|
||||
bne +
|
||||
cmp #$00
|
||||
beq ++
|
||||
|
||||
+ ldx #$98
|
||||
bit FAC+1
|
||||
bmi +
|
||||
|
||||
- dex
|
||||
asl FAC+3
|
||||
rol FAC+2
|
||||
rol FAC+1
|
||||
bpl -
|
||||
|
||||
+ stx FAC
|
||||
stz FAC+4
|
||||
stz FAC+5
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
@@ -157,7 +189,6 @@ _msg .text 13,"?rom 47+ required for val1",13,0
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
&uword AYINT_facmo = $c6 ; $c6/$c7 contain result of AYINT (See "basic.sym" kernal symbol file)
|
||||
|
||||
sub rnd() -> float {
|
||||
@@ -173,6 +204,14 @@ asmsub normalize(float value @FAC1) -> float @ FAC1 {
|
||||
}}
|
||||
}
|
||||
|
||||
; get the jiffy clock as a float
|
||||
asmsub time() -> float @ FAC1 {
|
||||
%asm {{
|
||||
jsr cbm.RDTIM
|
||||
jmp floats.FREADU24AXY
|
||||
}}
|
||||
}
|
||||
|
||||
%asminclude "library:c64/floats.asm"
|
||||
%asminclude "library:c64/floats_funcs.asm"
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
;
|
||||
; NOTE: the bitmap screen data is positioned in vram at $0:0000
|
||||
;
|
||||
; NOTE: In the future, these routines might be split out to separate modules, 1 for each screen mode,
|
||||
; so they can be optimized a lot better. There's already a "gfx_lores" module with a few routines for lores 256C mode.
|
||||
;
|
||||
; SCREEN MODE LIST:
|
||||
; mode 0 = reset back to default text mode
|
||||
; mode 1 = bitmap 320 x 240 x 256c (8 bpp)
|
||||
@@ -285,6 +288,9 @@ gfx2 {
|
||||
sub line(uword @zp x1, uword @zp y1, uword @zp x2, uword @zp y2, ubyte color) {
|
||||
; Bresenham algorithm.
|
||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||
|
||||
; NOTE: if you are using lores 256c (mode 1), the line routine in the fastline module can be used and is MUCH faster than this one.
|
||||
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||
cx16.r0 = x1
|
||||
@@ -479,12 +485,25 @@ gfx2 {
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
uword last_y3 = ycenter+radius
|
||||
uword last_y4 = ycenter-radius
|
||||
uword new_y3, new_y4
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, color)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, color)
|
||||
|
||||
new_y3 = ycenter+radius
|
||||
if new_y3 != last_y3 {
|
||||
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
|
||||
last_y3 = new_y3
|
||||
}
|
||||
new_y4 = ycenter-radius
|
||||
if new_y4 != last_y4 {
|
||||
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
|
||||
last_y4 = new_y4
|
||||
}
|
||||
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
@@ -493,6 +512,11 @@ gfx2 {
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
|
||||
; draw the final two spans
|
||||
yy--
|
||||
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
|
||||
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
|
||||
}
|
||||
|
||||
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, ubyte color) {
|
||||
@@ -502,12 +526,23 @@ gfx2 {
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
uword last_y3 = ycenter+radius
|
||||
uword last_y4 = ycenter-radius
|
||||
uword new_y3, new_y4
|
||||
|
||||
while radius>=yy {
|
||||
safe_horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, color)
|
||||
safe_horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, color)
|
||||
safe_horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, color)
|
||||
safe_horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, color)
|
||||
new_y3 = ycenter+radius
|
||||
if new_y3 != last_y3 {
|
||||
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
|
||||
last_y3 = new_y3
|
||||
}
|
||||
new_y4 = ycenter-radius
|
||||
if new_y4 != last_y4 {
|
||||
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
|
||||
last_y4 = new_y4
|
||||
}
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
@@ -516,6 +551,10 @@ gfx2 {
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
; draw the final two spans
|
||||
yy--
|
||||
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, color)
|
||||
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, color)
|
||||
}
|
||||
|
||||
sub plot(uword @zp xx, uword @zp yy, ubyte @zp color) {
|
||||
@@ -759,6 +798,7 @@ skip:
|
||||
cx16.VERA_CTRL = 1
|
||||
cx16.VERA_ADDR_H = cx16.r1L
|
||||
cx16.VERA_ADDR = cx16.r0
|
||||
cx16.VERA_CTRL = 0
|
||||
}
|
||||
|
||||
sub fill_scanline_left_8bpp() -> bool {
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
; optimized graphics routines for just the single screen mode: lores 320*240, 256c (8bpp)
|
||||
; bitmap image needs to start at VRAM addres $00000.
|
||||
; This is compatible with the CX16's screen mode 128. (void cx16.set_screen_mode(128))
|
||||
|
||||
|
||||
gfx_lores {
|
||||
|
||||
%option ignore_unused
|
||||
|
||||
sub set_screen_mode() {
|
||||
cx16.VERA_CTRL=0
|
||||
cx16.VERA_DC_VIDEO = (cx16.VERA_DC_VIDEO & %11001111) | %00100000 ; enable only layer 1
|
||||
cx16.VERA_DC_HSCALE = 64
|
||||
cx16.VERA_DC_VSCALE = 64
|
||||
cx16.VERA_L1_CONFIG = %00000111
|
||||
cx16.VERA_L1_MAPBASE = 0
|
||||
cx16.VERA_L1_TILEBASE = 0
|
||||
clear_screen(0)
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte color) {
|
||||
cx16.VERA_CTRL=0
|
||||
cx16.VERA_ADDR=0
|
||||
cx16.VERA_ADDR_H = 1<<4 ; 1 pixel auto increment
|
||||
repeat 240
|
||||
cs_innerloop320(color)
|
||||
cx16.VERA_ADDR=0
|
||||
cx16.VERA_ADDR_H = 0
|
||||
}
|
||||
|
||||
sub line(uword x1, ubyte y1, uword x2, ubyte y2, ubyte color) {
|
||||
; Bresenham algorithm.
|
||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||
; NOTE: this is about twice as fast as the kernal routine GRAPH_draw_line, and ~3-4 times as fast as gfx2.line()
|
||||
; it trades memory for speed (uses inline plot routine and multiplication lookup tables)
|
||||
;
|
||||
; NOTE: is currently still a regular 6502 routine, could likely be made much faster with the VeraFX line helper.
|
||||
|
||||
cx16.r3L = y2 ; ensure zeropage
|
||||
cx16.r1L = y1 ; ensure zeropage
|
||||
|
||||
if cx16.r1L > cx16.r3L {
|
||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||
cx16.r0 = x1
|
||||
x1 = x2
|
||||
x2 = cx16.r0
|
||||
cx16.r0L = cx16.r1L
|
||||
cx16.r1L = cx16.r3L
|
||||
cx16.r3L = cx16.r0L
|
||||
}
|
||||
word @zp dx = x2 as word
|
||||
word @zp dy = cx16.r3L
|
||||
dx -= x1
|
||||
dy -= cx16.r1L
|
||||
|
||||
if dx==0 {
|
||||
vertical_line(x1, cx16.r1L, lsb(dy)+1, color)
|
||||
return
|
||||
}
|
||||
if dy==0 {
|
||||
if x1>x2
|
||||
x1=x2
|
||||
horizontal_line(x1, cx16.r1L, abs(dx) as uword +1, color)
|
||||
return
|
||||
}
|
||||
|
||||
word @zp d = 0
|
||||
bool positive_ix = true
|
||||
if dx < 0 {
|
||||
dx = -dx
|
||||
positive_ix = false
|
||||
}
|
||||
word @zp dx2 = dx*2
|
||||
word @zp dy2 = dy*2
|
||||
|
||||
cx16.r0 = x1 ; ensure zeropage
|
||||
cx16.r2 = x2 ; ensure zeropage
|
||||
|
||||
cx16.VERA_CTRL = 0
|
||||
if dx >= dy {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
plot()
|
||||
if cx16.r0==cx16.r2
|
||||
return
|
||||
cx16.r0++
|
||||
d += dy2
|
||||
if d > dx {
|
||||
cx16.r1L++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot()
|
||||
if cx16.r0==cx16.r2
|
||||
return
|
||||
cx16.r0--
|
||||
d += dy2
|
||||
if d > dx {
|
||||
cx16.r1L++
|
||||
d -= dx2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if positive_ix {
|
||||
repeat {
|
||||
plot()
|
||||
if cx16.r1L == cx16.r3L
|
||||
return
|
||||
cx16.r1L++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
cx16.r0++
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
} else {
|
||||
repeat {
|
||||
plot()
|
||||
if cx16.r1L == cx16.r3L
|
||||
return
|
||||
cx16.r1L++
|
||||
d += dx2
|
||||
if d > dy {
|
||||
cx16.r0--
|
||||
d -= dy2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asmsub plot() {
|
||||
; x in r0, y in r1, color.
|
||||
%asm {{
|
||||
ldy cx16.r1L
|
||||
clc
|
||||
lda times320_lo,y
|
||||
adc cx16.r0L
|
||||
sta cx16.VERA_ADDR_L
|
||||
lda times320_mid,y
|
||||
adc cx16.r0H
|
||||
sta cx16.VERA_ADDR_M
|
||||
lda #0
|
||||
adc times320_hi,y
|
||||
sta cx16.VERA_ADDR_H
|
||||
lda p8v_color
|
||||
sta cx16.VERA_DATA0
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
%asm {{
|
||||
|
||||
; multiplication by 320 lookup table
|
||||
times320 := 320*range(240)
|
||||
|
||||
times320_lo .byte <times320
|
||||
times320_mid .byte >times320
|
||||
times320_hi .byte `times320
|
||||
|
||||
}}
|
||||
}
|
||||
|
||||
sub horizontal_line(uword xx, ubyte yy, uword length, ubyte color) {
|
||||
if length==0
|
||||
return
|
||||
vera_setaddr(xx, yy)
|
||||
; set vera auto-increment to 1 pixel
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (1<<4)
|
||||
|
||||
%asm {{
|
||||
lda p8v_color
|
||||
ldx p8v_length+1
|
||||
beq +
|
||||
ldy #0
|
||||
- sta cx16.VERA_DATA0
|
||||
iny
|
||||
bne -
|
||||
dex
|
||||
bne -
|
||||
+ ldy p8v_length ; remaining
|
||||
beq +
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
+
|
||||
}}
|
||||
}
|
||||
|
||||
sub vertical_line(uword xx, ubyte yy, ubyte lheight, ubyte color) {
|
||||
vera_setaddr(xx,yy)
|
||||
; set vera auto-increment to 320 pixel increment (=next line)
|
||||
cx16.VERA_ADDR_H = cx16.VERA_ADDR_H & %00000111 | (14<<4)
|
||||
%asm {{
|
||||
ldy p8v_lheight
|
||||
beq +
|
||||
lda p8v_color
|
||||
- sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
+
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
asmsub cs_innerloop320(ubyte color @A) clobbers(Y) {
|
||||
; using verafx 32 bits writes here would make this faster but it's safer to
|
||||
; use verafx only explicitly when you know what you're doing.
|
||||
%asm {{
|
||||
ldy #40
|
||||
- sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
sta cx16.VERA_DATA0
|
||||
dey
|
||||
bne -
|
||||
rts
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub vera_setaddr(uword xx @R0, ubyte yy @R1) {
|
||||
; set the correct vera start address (no auto increment yet!)
|
||||
%asm {{
|
||||
jsr p8s_line.p8s_plot
|
||||
}}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,83 +76,30 @@ graphics {
|
||||
|
||||
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
|
||||
|
||||
;cx16.r0 = xcenter - radius/2
|
||||
;cx16.r1 = ycenter - radius/2
|
||||
;cx16.r2 = radius*2
|
||||
;cx16.r3 = radius*2
|
||||
;cx16.GRAPH_draw_oval(false) ; currently this call is not implemented on cx16, does a BRK
|
||||
|
||||
; Midpoint algorithm
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp xx = radius
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-xx
|
||||
|
||||
while xx>=yy {
|
||||
cx16.r0 = xcenter + xx
|
||||
cx16.r1 = ycenter + yy
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - xx
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter + xx
|
||||
cx16.r1 = ycenter - yy
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - xx
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter + yy
|
||||
cx16.r1 = ycenter + xx
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - yy
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter + yy
|
||||
cx16.r1 = ycenter - xx
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
cx16.r0 = xcenter - yy
|
||||
FB_cursor_position2()
|
||||
cx16.FB_set_pixel(stroke_color)
|
||||
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
xx--
|
||||
decisionOver2 -= xx*$0002
|
||||
}
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
cx16.GRAPH_draw_oval(xcenter - radius, ycenter - radius, radius*2, radius*2, false)
|
||||
}
|
||||
|
||||
sub disc(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||
; Warning: NO BOUNDS CHECKS. Make sure circle fits in the screen.
|
||||
; Midpoint algorithm, filled
|
||||
|
||||
if radius==0
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word decisionOver2 = (1 as word)-radius
|
||||
cx16.GRAPH_draw_oval(xcenter - radius, ycenter - radius, radius*2, radius*2, true)
|
||||
}
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*2+1)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*2+1)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*2+1)
|
||||
sub oval(uword xcenter, ubyte ycenter, uword h_radius, ubyte v_radius) {
|
||||
; specific for the X16 which has a kernal routine that can draw ovals
|
||||
if h_radius==0 or v_radius==0
|
||||
return
|
||||
cx16.GRAPH_draw_oval(xcenter - h_radius, ycenter - v_radius, h_radius*2, v_radius*2, false)
|
||||
}
|
||||
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
decisionOver2 -= radius*$0002
|
||||
}
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
sub filled_oval(uword xcenter, ubyte ycenter, uword h_radius, ubyte v_radius) {
|
||||
; specific for the X16 which has a kernal routine that can draw ovals
|
||||
if h_radius==0 or v_radius==0
|
||||
return
|
||||
cx16.GRAPH_draw_oval(xcenter - h_radius, ycenter - v_radius, h_radius*2, v_radius*2, true)
|
||||
}
|
||||
|
||||
inline asmsub plot(uword plotx @R0, uword ploty @R1) clobbers(A, X, Y) {
|
||||
|
||||
@@ -32,7 +32,7 @@ monogfx {
|
||||
width = 320
|
||||
height = 240
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(0)
|
||||
clear_screen(false)
|
||||
}
|
||||
|
||||
sub hires() {
|
||||
@@ -47,7 +47,7 @@ monogfx {
|
||||
width = 640
|
||||
height = 480
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(0)
|
||||
clear_screen(false)
|
||||
}
|
||||
|
||||
sub textmode() {
|
||||
@@ -61,16 +61,16 @@ monogfx {
|
||||
mode = dm
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte color) {
|
||||
sub clear_screen(bool draw) {
|
||||
position(0, 0)
|
||||
when width {
|
||||
320 -> {
|
||||
repeat 240/2/8
|
||||
cs_innerloop640(color)
|
||||
cs_innerloop640(draw)
|
||||
}
|
||||
640 -> {
|
||||
repeat 480/8
|
||||
cs_innerloop640(color)
|
||||
cs_innerloop640(draw)
|
||||
}
|
||||
}
|
||||
position(0, 0)
|
||||
@@ -90,8 +90,8 @@ monogfx {
|
||||
}
|
||||
|
||||
sub fillrect(uword xx, uword yy, uword rwidth, uword rheight, bool draw) {
|
||||
; Draw a filled rectangle of the given size and color.
|
||||
; To fill the whole screen, use clear_screen(color) instead - it is much faster.
|
||||
; Draw a filled rectangle of the given size.
|
||||
; To fill the whole screen, use clear_screen(draw) instead - it is much faster.
|
||||
if rwidth==0
|
||||
return
|
||||
repeat rheight {
|
||||
@@ -563,12 +563,23 @@ drawmode: ora cx16.r15L
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
uword last_y3 = ycenter+radius
|
||||
uword last_y4 = ycenter-radius
|
||||
uword new_y3, new_y4
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
|
||||
new_y3 = ycenter+radius
|
||||
if new_y3 != last_y3 {
|
||||
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
last_y3 = new_y3
|
||||
}
|
||||
new_y4 = ycenter-radius
|
||||
if new_y4 != last_y4 {
|
||||
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
last_y4 = new_y4
|
||||
}
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
@@ -577,6 +588,10 @@ drawmode: ora cx16.r15L
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
; draw the final two spans
|
||||
yy--
|
||||
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
}
|
||||
|
||||
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||
@@ -586,12 +601,23 @@ drawmode: ora cx16.r15L
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
uword last_y3 = ycenter+radius
|
||||
uword last_y4 = ycenter-radius
|
||||
uword new_y3, new_y4
|
||||
|
||||
while radius>=yy {
|
||||
safe_horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
|
||||
new_y3 = ycenter+radius
|
||||
if new_y3 != last_y3 {
|
||||
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
last_y3 = new_y3
|
||||
}
|
||||
new_y4 = ycenter-radius
|
||||
if new_y4 != last_y4 {
|
||||
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
last_y4 = new_y4
|
||||
}
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
@@ -600,6 +626,10 @@ drawmode: ora cx16.r15L
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
; draw the final two spans
|
||||
yy--
|
||||
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
}
|
||||
|
||||
sub plot(uword @zp xx, uword @zp yy, bool @zp draw) {
|
||||
@@ -970,7 +1000,7 @@ cdraw_mod2 ora cx16.VERA_DATA1
|
||||
}
|
||||
}
|
||||
|
||||
asmsub cs_innerloop640(ubyte color @A) clobbers(Y) {
|
||||
asmsub cs_innerloop640(bool draw @A) clobbers(Y) {
|
||||
; using verafx 32 bits writes here would make this faster but it's safer to
|
||||
; use verafx only explicitly when you know what you're doing.
|
||||
%asm {{
|
||||
|
||||
@@ -23,7 +23,7 @@ sprites {
|
||||
ubyte databank, uword dataaddr,
|
||||
ubyte width_flag, ubyte height_flag,
|
||||
ubyte colors_flag, ubyte palette_offset) {
|
||||
hide(spritenum)
|
||||
pos(spritenum, -64, -64) ; move sprite off-screen initially
|
||||
cx16.VERA_DC_VIDEO |= %01000000 ; enable sprites globally
|
||||
dataaddr >>= 5
|
||||
dataaddr |= (databank as uword)<<11
|
||||
@@ -138,7 +138,15 @@ sprites {
|
||||
}
|
||||
|
||||
sub hide(ubyte spritenum) {
|
||||
pos(spritenum, -64, -64)
|
||||
cx16.vpoke_and(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11110011)
|
||||
}
|
||||
|
||||
sub show(ubyte spritenum) {
|
||||
cx16.vpoke_or(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %00001100)
|
||||
}
|
||||
|
||||
sub zdepth(ubyte spritenum, ubyte depth) {
|
||||
cx16.vpoke_mask(1, VERA_SPRITEREGS + 6 + spritenum*$0008, %11110011, depth<<2)
|
||||
}
|
||||
|
||||
sub flipx(ubyte spritenum, bool flipped) {
|
||||
|
||||
@@ -554,7 +554,8 @@ const ubyte EXTAPI_ps2data_raw = $09
|
||||
const ubyte EXTAPI_cursor_blink = $0A
|
||||
const ubyte EXTAPI_led_update = $0B
|
||||
const ubyte EXTAPI_mouse_set_position = $0C
|
||||
const ubyte EXTAPI_scnsiz = $0D ; rom R48+
|
||||
const ubyte EXTAPI_scnsiz = $0D
|
||||
const ubyte EXTAPI_kbd_leds = $0E
|
||||
|
||||
; extapi16 call numbers
|
||||
const ubyte EXTAPI16_test = $00
|
||||
@@ -603,6 +604,14 @@ asmsub mouse_pos() -> ubyte @A, uword @R0, uword @R1, byte @X {
|
||||
}}
|
||||
}
|
||||
|
||||
sub mouse_present() -> bool {
|
||||
; -- check if a mouse is connected to the machine
|
||||
cx16.r0L, void = cx16.i2c_read_byte($42, $22) ; $22 = I2C_GET_MOUSE_DEVICE_ID
|
||||
if_cs
|
||||
return false
|
||||
return cx16.r0L != $fc ; $fc = BAT_FAIL
|
||||
}
|
||||
|
||||
; shims for the kernal routines called via the extapi call:
|
||||
|
||||
asmsub mouse_set_pos(uword xpos @R0, uword ypos @R1) clobbers(X) {
|
||||
|
||||
@@ -233,7 +233,7 @@ math {
|
||||
sub direction(ubyte x1, ubyte y1, ubyte x2, ubyte y2) -> ubyte {
|
||||
; From a pair of positive coordinates, calculate discrete direction between 0 and 23 into A.
|
||||
; This adjusts the atan() result so that the direction N is centered on the angle=N instead of having it as a boundary
|
||||
ubyte angle = atan2(x1, y1, x2, y2) - 256/48
|
||||
ubyte angle = atan2(x1, y1, x2, y2) - (256/48 as ubyte)
|
||||
return 23-lsb(mkword(angle,0) / 2730)
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ monogfx {
|
||||
width = 320
|
||||
height = 240
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(0)
|
||||
clear_screen(false)
|
||||
}
|
||||
|
||||
sub hires() {
|
||||
@@ -32,7 +32,7 @@ monogfx {
|
||||
width = 640
|
||||
height = 480
|
||||
mode = MODE_NORMAL
|
||||
clear_screen(0)
|
||||
clear_screen(false)
|
||||
}
|
||||
|
||||
sub textmode() {
|
||||
@@ -43,9 +43,10 @@ monogfx {
|
||||
mode = dm
|
||||
}
|
||||
|
||||
sub clear_screen(ubyte color) {
|
||||
if color!=0
|
||||
color=255
|
||||
sub clear_screen(bool draw) {
|
||||
ubyte color = 0
|
||||
if draw
|
||||
color = 255
|
||||
sys.gfx_clear(color)
|
||||
}
|
||||
|
||||
@@ -64,7 +65,7 @@ monogfx {
|
||||
|
||||
sub fillrect(uword xx, uword yy, uword rwidth, uword rheight, bool draw) {
|
||||
; Draw a filled rectangle of the given size and color.
|
||||
; To fill the whole screen, use clear_screen(color) instead - it is much faster.
|
||||
; To fill the whole screen, use clear_screen(draw) instead - it is much faster.
|
||||
if rwidth==0
|
||||
return
|
||||
repeat rheight {
|
||||
@@ -300,12 +301,23 @@ monogfx {
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
uword last_y3 = ycenter+radius
|
||||
uword last_y4 = ycenter-radius
|
||||
uword new_y3, new_y4
|
||||
|
||||
while radius>=yy {
|
||||
horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
|
||||
horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
|
||||
new_y3 = ycenter+radius
|
||||
if new_y3 != last_y3 {
|
||||
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
last_y3 = new_y3
|
||||
}
|
||||
new_y4 = ycenter-radius
|
||||
if new_y4 != last_y4 {
|
||||
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
last_y4 = new_y4
|
||||
}
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
@@ -314,6 +326,10 @@ monogfx {
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
; draw the final two spans
|
||||
yy--
|
||||
horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
}
|
||||
|
||||
sub safe_disc(uword @zp xcenter, uword @zp ycenter, ubyte @zp radius, bool draw) {
|
||||
@@ -323,12 +339,23 @@ monogfx {
|
||||
return
|
||||
ubyte @zp yy = 0
|
||||
word @zp decisionOver2 = (1 as word)-radius
|
||||
uword last_y3 = ycenter+radius
|
||||
uword last_y4 = ycenter-radius
|
||||
uword new_y3, new_y4
|
||||
|
||||
while radius>=yy {
|
||||
safe_horizontal_line(xcenter-radius, ycenter+yy, radius*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-radius, ycenter-yy, radius*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-yy, ycenter+radius, yy*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-yy, ycenter-radius, yy*$0002+1, draw)
|
||||
new_y3 = ycenter+radius
|
||||
if new_y3 != last_y3 {
|
||||
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
last_y3 = new_y3
|
||||
}
|
||||
new_y4 = ycenter-radius
|
||||
if new_y4 != last_y4 {
|
||||
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
last_y4 = new_y4
|
||||
}
|
||||
yy++
|
||||
if decisionOver2>=0 {
|
||||
radius--
|
||||
@@ -337,6 +364,10 @@ monogfx {
|
||||
decisionOver2 += yy*$0002
|
||||
decisionOver2++
|
||||
}
|
||||
; draw the final two spans
|
||||
yy--
|
||||
safe_horizontal_line(xcenter-yy, last_y3, yy*$0002+1, draw)
|
||||
safe_horizontal_line(xcenter-yy, last_y4, yy*$0002+1, draw)
|
||||
}
|
||||
|
||||
sub plot(uword @zp xx, uword @zp yy, bool @zp draw) {
|
||||
|
||||
@@ -19,6 +19,11 @@ import kotlin.system.exitProcess
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
// NOTE: if your compiler/IDE complains here about "Unresolved reference: buildversion",
|
||||
// it means that you have to run the gradle task once to generate this file.
|
||||
// Do that with this command: gradle createVersionFile
|
||||
|
||||
println("\nProg8 compiler v${prog8.buildversion.VERSION} by Irmen de Jong (irmen@razorvine.net)")
|
||||
if('-' in prog8.buildversion.VERSION) {
|
||||
println("Prerelease version from git commit ${prog8.buildversion.GIT_SHA.take(8)} in branch ${prog8.buildversion.GIT_BRANCH}")
|
||||
@@ -45,7 +50,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
val dumpVariables by cli.option(ArgType.Boolean, fullName = "dumpvars", description = "print a dump of the variables in the program")
|
||||
val dumpSymbols by cli.option(ArgType.Boolean, fullName = "dumpsymbols", description = "print a dump of the variable declarations and subroutine signatures")
|
||||
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||
val noStrictBool by cli.option(ArgType.Boolean, fullName = "nostrictbool", description = "allow implicit conversions between bool and bytes")
|
||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform code optimizations")
|
||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||
@@ -178,7 +182,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
noStrictBool != true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
@@ -259,7 +262,6 @@ private fun compileMain(args: Array<String>): Boolean {
|
||||
breakpointCpuInstruction,
|
||||
printAst1 == true,
|
||||
printAst2 == true,
|
||||
noStrictBool != true,
|
||||
processedSymbols,
|
||||
srcdirs,
|
||||
outputPath
|
||||
|
||||
@@ -54,7 +54,6 @@ class CompilerArguments(val filepath: Path,
|
||||
val breakpointCpuInstruction: String?,
|
||||
val printAst1: Boolean,
|
||||
val printAst2: Boolean,
|
||||
val strictBool: Boolean,
|
||||
val symbolDefs: Map<String, String>,
|
||||
val sourceDirs: List<String> = emptyList(),
|
||||
val outputDir: Path = Path(""),
|
||||
@@ -91,7 +90,6 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
|
||||
splitWordArrays = args.splitWordArrays
|
||||
outputDir = args.outputDir.normalize()
|
||||
symbolDefs = args.symbolDefs
|
||||
strictBool = args.strictBool
|
||||
}
|
||||
resultingProgram = program
|
||||
importedFiles = imported
|
||||
@@ -412,7 +410,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
|
||||
errors.report()
|
||||
program.reorderStatements(errors)
|
||||
errors.report()
|
||||
program.desugaring(errors, compilerOptions)
|
||||
program.desugaring(errors)
|
||||
errors.report()
|
||||
program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
@@ -438,7 +436,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
removeUnusedCode(program, errors,compilerOptions)
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = program.simplifyExpressions(errors, compilerOptions)
|
||||
val optsDone1 = program.simplifyExpressions(errors)
|
||||
val optsDone2 = program.optimizeStatements(errors, functions, compilerOptions)
|
||||
val optsDone3 = program.inlineSubroutines(compilerOptions)
|
||||
program.constantFold(errors, compilerOptions) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
@@ -452,7 +450,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
removeUnusedCode(program, errors, compilerOptions)
|
||||
if(errors.noErrors()) {
|
||||
// last round of optimizations because constFold may have enabled more...
|
||||
program.simplifyExpressions(errors, compilerOptions)
|
||||
program.simplifyExpressions(errors)
|
||||
program.optimizeStatements(errors, functions, compilerOptions)
|
||||
program.constantFold(errors, compilerOptions) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
}
|
||||
@@ -461,7 +459,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
}
|
||||
|
||||
private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.desugaring(errors, compilerOptions)
|
||||
program.desugaring(errors)
|
||||
program.addTypecasts(errors, compilerOptions)
|
||||
errors.report()
|
||||
program.variousCleanups(errors, compilerOptions)
|
||||
|
||||
@@ -163,9 +163,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(ifElse: IfElse) {
|
||||
if(!ifElse.condition.inferType(program).isBool) {
|
||||
val ctype = ifElse.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(compilerOptions.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", ifElse.condition.position)
|
||||
errors.err("condition should be a boolean", ifElse.condition.position)
|
||||
}
|
||||
|
||||
val constvalue = ifElse.condition.constValue(program)
|
||||
@@ -506,9 +504,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(untilLoop: UntilLoop) {
|
||||
if(!untilLoop.condition.inferType(program).isBool) {
|
||||
val ctype = untilLoop.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(compilerOptions.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", untilLoop.condition.position)
|
||||
errors.err("condition should be a boolean", untilLoop.condition.position)
|
||||
}
|
||||
|
||||
super.visit(untilLoop)
|
||||
@@ -516,9 +512,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(whileLoop: WhileLoop) {
|
||||
if(!whileLoop.condition.inferType(program).isBool) {
|
||||
val ctype = whileLoop.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(compilerOptions.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
}
|
||||
|
||||
super.visit(whileLoop)
|
||||
@@ -780,12 +774,8 @@ internal class AstChecker(private val program: Program,
|
||||
if (iDt isnot decl.datatype) {
|
||||
if(decl.isArray) {
|
||||
val eltDt = ArrayToElementTypes.getValue(decl.datatype)
|
||||
if(iDt isnot eltDt) {
|
||||
if(compilerOptions.strictBool)
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
else if(!(iDt.isBool && eltDt==DataType.UBYTE || iDt.istype(DataType.UBYTE) && eltDt==DataType.BOOL))
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
}
|
||||
if(iDt isnot eltDt)
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
} else {
|
||||
if(!(iDt.isBool && decl.datatype==DataType.UBYTE || iDt.istype(DataType.UBYTE) && decl.datatype==DataType.BOOL))
|
||||
err("initialisation value has incompatible type ($iDt) for the variable (${decl.datatype})")
|
||||
@@ -1048,8 +1038,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
else if(expr.operator == "not") {
|
||||
if(dt!=DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || dt !in ByteDatatypes)
|
||||
errors.err("logical not is for booleans", expr.position)
|
||||
errors.err("logical not is for booleans", expr.position)
|
||||
}
|
||||
}
|
||||
super.visit(expr)
|
||||
@@ -1168,8 +1157,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
if(expr.operator in LogicalOperators) {
|
||||
if (leftDt != DataType.BOOL || rightDt != DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || leftDt !in ByteDatatypes || rightDt !in ByteDatatypes)
|
||||
errors.err("logical operator requires boolean operands", expr.right.position)
|
||||
errors.err("logical operator requires boolean operands", expr.right.position)
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1248,7 +1236,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.warn("sgn() of unsigned type is always 0 or 1, this is perhaps not what was intended", functionCallExpr.args.first().position)
|
||||
}
|
||||
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallExpr, program, compilerOptions)
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallExpr, program)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
|
||||
@@ -1339,7 +1327,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
}
|
||||
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, program, compilerOptions)
|
||||
val error = VerifyFunctionArgTypes.checkTypes(functionCallStatement, program)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
|
||||
@@ -1727,8 +1715,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
DataType.BOOL -> {
|
||||
if (value.type!=DataType.BOOL) {
|
||||
if (compilerOptions.strictBool || value.type !in ByteDatatypes)
|
||||
err("type of value ${value.type} doesn't match target $targetDt")
|
||||
err("type of value ${value.type} doesn't match target $targetDt")
|
||||
}
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
@@ -1829,12 +1816,10 @@ internal class AstChecker(private val program: Program,
|
||||
// this is allowed: a pass-by-reference datatype into a uword (pointer value).
|
||||
}
|
||||
else if(sourceDatatype==DataType.BOOL && targetDatatype!=DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || targetDatatype !in ByteDatatypes)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else if(targetDatatype==DataType.BOOL && sourceDatatype!=DataType.BOOL) {
|
||||
if(compilerOptions.strictBool || sourceDatatype !in ByteDatatypes)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
}
|
||||
else {
|
||||
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
|
||||
|
||||
@@ -92,8 +92,8 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
|
||||
caster.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
|
||||
val desugar = CodeDesugarer(this, options, errors)
|
||||
fun Program.desugaring(errors: IErrorReporter) {
|
||||
val desugar = CodeDesugarer(this, errors)
|
||||
desugar.visit(this)
|
||||
while(errors.noErrors() && desugar.applyModifications()>0)
|
||||
desugar.visit(this)
|
||||
|
||||
@@ -48,22 +48,30 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
if(decl.name in BuiltinFunctions)
|
||||
errors.err("builtin function cannot be redefined", decl.position)
|
||||
|
||||
if(decl.names.size<2) {
|
||||
val existingInSameScope = decl.definingScope.lookup(listOf(decl.name))
|
||||
fun checkNameForErrors(name: String) {
|
||||
val existingInSameScope = decl.definingScope.lookup(listOf(name))
|
||||
if (existingInSameScope != null && existingInSameScope !== decl)
|
||||
nameError(decl.name, decl.position, existingInSameScope)
|
||||
nameError(name, decl.position, existingInSameScope)
|
||||
|
||||
val existingOuter = decl.parent.definingScope.lookup(listOf(decl.name))
|
||||
val existingOuter = decl.parent.definingScope.lookup(listOf(name))
|
||||
if (existingOuter != null && existingOuter !== decl) {
|
||||
if (existingOuter is VarDecl) {
|
||||
if (existingOuter.parent !== decl.parent)
|
||||
nameShadowWarning(decl.name, decl.position, existingOuter)
|
||||
nameShadowWarning(name, decl.position, existingOuter)
|
||||
else
|
||||
nameError(decl.name, decl.position, existingOuter)
|
||||
nameError(name, decl.position, existingOuter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(decl.names.size<2) {
|
||||
checkNameForErrors(decl.name)
|
||||
}
|
||||
else {
|
||||
for (name in decl.names)
|
||||
checkNameForErrors(name)
|
||||
}
|
||||
|
||||
if(decl.definingBlock.name==decl.name)
|
||||
nameError(decl.name, decl.position, decl.definingBlock)
|
||||
if(decl.definingSubroutine?.name==decl.name)
|
||||
@@ -72,6 +80,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
super.visit(decl)
|
||||
}
|
||||
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
if(subroutine.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
|
||||
@@ -8,7 +8,7 @@ import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class CodeDesugarer(val program: Program, private val options: CompilationOptions, private val errors: IErrorReporter) : AstWalker() {
|
||||
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
|
||||
// Some more code shuffling to simplify the Ast that the codegenerator has to process.
|
||||
// Several changes have already been done by the StatementReorderer !
|
||||
@@ -123,9 +123,7 @@ if not CONDITION
|
||||
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
if(!whileLoop.condition.inferType(program).isBool) {
|
||||
val ctype = whileLoop.condition.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(options.strictBool || ctype !in ByteDatatypes)
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
errors.err("condition should be a boolean", whileLoop.condition.position)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -11,9 +11,9 @@ import prog8.ast.statements.*
|
||||
import prog8.code.ast.*
|
||||
import prog8.code.core.*
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
import java.io.File
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.isRegularFile
|
||||
import java.io.File
|
||||
|
||||
|
||||
/**
|
||||
@@ -623,11 +623,10 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
}
|
||||
|
||||
when(srcCheck.iterable) {
|
||||
is IdentifierReference -> {
|
||||
is IdentifierReference, is ArrayLiteral -> {
|
||||
val check = PtContainmentCheck(srcCheck.position)
|
||||
check.add(transformExpression(srcCheck.element))
|
||||
val iterable = transformExpression(srcCheck.iterable)
|
||||
check.add(iterable)
|
||||
check.add(transformExpression(srcCheck.iterable))
|
||||
return check
|
||||
}
|
||||
is RangeExpression -> {
|
||||
|
||||
@@ -4,19 +4,14 @@ import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.ArrayLiteral
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.StringLiteral
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.WhenChoice
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.NumericDatatypesWithBoolean
|
||||
import prog8.code.core.SplitWordArrayTypes
|
||||
import prog8.code.ast.PtContainmentCheck
|
||||
import prog8.code.core.*
|
||||
|
||||
|
||||
internal class LiteralsToAutoVars(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
@@ -54,6 +49,16 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
}
|
||||
} else {
|
||||
val arrayDt = array.guessDatatype(program)
|
||||
val elementDt = ArrayToElementTypes.getValue(arrayDt.getOr(DataType.UNDEFINED))
|
||||
val maxSize = when(elementDt) {
|
||||
in ByteDatatypesWithBoolean -> PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_BYTE
|
||||
in WordDatatypes -> PtContainmentCheck.MAX_SIZE_FOR_INLINE_CHECKS_WORD
|
||||
else -> 0
|
||||
}
|
||||
if(parent is ContainmentCheck && array.value.size <= maxSize) {
|
||||
// keep the array in the containmentcheck inline
|
||||
return noModifications
|
||||
}
|
||||
if(arrayDt.isKnown) {
|
||||
val parentAssign = parent as? Assignment
|
||||
val targetDt = parentAssign?.target?.inferType(program) ?: arrayDt
|
||||
|
||||
@@ -140,7 +140,7 @@ internal class StatementReorderer(
|
||||
// change 'str' and 'ubyte[]' parameters into 'uword' (just treat it as an address)
|
||||
val stringParams = subroutine.parameters.filter { it.type==DataType.STR || it.type==DataType.ARRAY_UB }
|
||||
val parameterChanges = stringParams.map {
|
||||
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
|
||||
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.zp, it.position)
|
||||
IAstModification.ReplaceNode(it, uwordParam, subroutine)
|
||||
}
|
||||
// change 'str' and 'ubyte[]' return types into 'uword' (just treat it as an address)
|
||||
|
||||
@@ -24,11 +24,6 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val valueDt = declValue.inferType(program)
|
||||
if(valueDt isnot decl.datatype) {
|
||||
|
||||
if(decl.isArray && !options.strictBool) {
|
||||
if(tryConvertBooleanArrays(decl, declValue, parent))
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(valueDt.isInteger && decl.isArray) {
|
||||
if(decl.datatype == DataType.ARRAY_BOOL) {
|
||||
val integer = declValue.constValue(program)?.number
|
||||
@@ -54,69 +49,6 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun tryConvertBooleanArrays(decl: VarDecl, declValue: Expression, parent: Node): Boolean {
|
||||
val valueNumber = declValue.constValue(program)
|
||||
val valueArray = declValue as? ArrayLiteral
|
||||
when (decl.datatype) {
|
||||
DataType.ARRAY_BOOL -> {
|
||||
if(valueNumber!=null) {
|
||||
decl.value = NumericLiteral.fromBoolean(valueNumber.number!=0.0, declValue.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
} else if(valueArray!=null) {
|
||||
val newArray = valueArray.value.map {
|
||||
if(it.inferType(program).isBytes) {
|
||||
TypecastExpression(it, DataType.BOOL, false, it.position)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
decl.value = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_BOOL), newArray.toTypedArray(), valueArray.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
if(valueNumber!=null) {
|
||||
decl.value = NumericLiteral(DataType.BYTE, if(valueNumber.asBooleanValue) 1.0 else 0.0, declValue.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
} else if(valueArray!=null) {
|
||||
val newArray = valueArray.value.map {
|
||||
if(it.inferType(program).isBool) {
|
||||
TypecastExpression(it, DataType.BYTE, false, it.position)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
decl.value = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_B), newArray.toTypedArray(), valueArray.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_UB -> {
|
||||
if(valueNumber!=null) {
|
||||
decl.value = NumericLiteral(DataType.UBYTE, if(valueNumber.asBooleanValue) 1.0 else 0.0, declValue.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
} else if(valueArray!=null) {
|
||||
val newArray = valueArray.value.map {
|
||||
if(it.inferType(program).isBool) {
|
||||
TypecastExpression(it, DataType.UBYTE, false, it.position)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
decl.value = ArrayLiteral(InferredTypes.InferredType.known(DataType.ARRAY_UB), newArray.toTypedArray(), valueArray.position)
|
||||
decl.linkParents(parent)
|
||||
return true
|
||||
}
|
||||
}
|
||||
else -> { /* no casting possible */ }
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
@@ -155,26 +87,6 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
if(leftDt!=rightDt) {
|
||||
if(!options.strictBool) {
|
||||
if (expr.operator in LogicalOperators) {
|
||||
if (leftDt.isBool) {
|
||||
val cast = TypecastExpression(expr.right, DataType.BOOL, false, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
} else {
|
||||
val cast = TypecastExpression(expr.left, DataType.BOOL, false, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
}
|
||||
} else {
|
||||
if(leftDt.isBool && rightDt.isBytes) {
|
||||
val cast = TypecastExpression(expr.left, rightDt.getOr(DataType.UNDEFINED), false, expr.left.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.left, cast, expr))
|
||||
} else if(rightDt.isBool && leftDt.isBytes) {
|
||||
val cast = TypecastExpression(expr.right, leftDt.getOr(DataType.UNDEFINED), false, expr.right.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr.right, cast, expr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convert a negative operand for bitwise operator to the 2's complement positive number instead
|
||||
if(expr.operator in BitwiseOperators && leftDt.isInteger && rightDt.isInteger) {
|
||||
if(leftCv!=null && leftCv.number<0) {
|
||||
@@ -278,42 +190,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
val cvalue = assignment.value.constValue(program)
|
||||
if(cvalue!=null) {
|
||||
val number = cvalue.number
|
||||
// more complex comparisons if the type is different, but the constant value is compatible
|
||||
if(!options.strictBool) {
|
||||
if (targettype == DataType.BOOL && valuetype in ByteDatatypes) {
|
||||
val cast = NumericLiteral.fromBoolean(number!=0.0, cvalue.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
if (targettype in ByteDatatypes && valuetype == DataType.BOOL) {
|
||||
val cast = NumericLiteral(targettype, if(cvalue.asBooleanValue) 1.0 else 0.0, cvalue.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
}
|
||||
if (valuetype == DataType.BYTE && targettype == DataType.UBYTE) {
|
||||
if(number>0)
|
||||
return castLiteral(cvalue)
|
||||
} else if (valuetype == DataType.WORD && targettype == DataType.UWORD) {
|
||||
if(number>0)
|
||||
return castLiteral(cvalue)
|
||||
} else if (valuetype == DataType.UBYTE && targettype == DataType.BYTE) {
|
||||
if(number<0x80)
|
||||
return castLiteral(cvalue)
|
||||
} else if (valuetype == DataType.UWORD && targettype == DataType.WORD) {
|
||||
if(number<0x8000)
|
||||
return castLiteral(cvalue)
|
||||
}
|
||||
} else {
|
||||
if(!options.strictBool) {
|
||||
if (targettype == DataType.BOOL && valuetype in ByteDatatypes) {
|
||||
val cast = TypecastExpression(assignment.value, targettype, false, assignment.value.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
if (targettype in ByteDatatypes && valuetype == DataType.BOOL) {
|
||||
val cast = TypecastExpression(assignment.value, targettype, false, assignment.value.position)
|
||||
return listOf(IAstModification.ReplaceNode(assignment.value, cast, assignment))
|
||||
}
|
||||
}
|
||||
return castLiteral(cvalue)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,8 +242,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
} else if(argDt isAssignableTo targetDt) {
|
||||
if(argDt!=DataType.STR || targetDt!=DataType.UWORD)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
} else if(!options.strictBool && targetDt in ByteDatatypes && argDt==DataType.BOOL)
|
||||
addTypecastOrCastedValueModification(modifications, it.second, targetDt, call as Node)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val identifier = it.second as? IdentifierReference
|
||||
@@ -432,18 +308,6 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
if(subroutine.returntypes.size==1) {
|
||||
val subReturnType = subroutine.returntypes.first()
|
||||
val returnDt = returnValue.inferType(program)
|
||||
|
||||
if(!options.strictBool) {
|
||||
if(subReturnType==DataType.BOOL && returnDt.isBytes) {
|
||||
val cast = TypecastExpression(returnValue, DataType.BOOL, false, returnValue.position)
|
||||
return listOf(IAstModification.ReplaceNode(returnValue, cast, returnStmt))
|
||||
}
|
||||
if(subReturnType in ByteDatatypes && returnDt.isBool) {
|
||||
val cast = TypecastExpression(returnValue, subReturnType, false, returnValue.position)
|
||||
return listOf(IAstModification.ReplaceNode(returnValue, cast, returnStmt))
|
||||
}
|
||||
}
|
||||
|
||||
if (returnDt istype subReturnType or returnDt.isNotAssignableTo(subReturnType))
|
||||
return noModifications
|
||||
if (returnValue is NumericLiteral) {
|
||||
@@ -466,7 +330,7 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val values = whenChoice.values
|
||||
values?.toTypedArray()?.withIndex()?.forEach { (index, value) ->
|
||||
val valueDt = value.inferType(program)
|
||||
if(valueDt!=conditionDt) {
|
||||
if(valueDt.isKnown && valueDt!=conditionDt) {
|
||||
val castedValue = value.typecastTo(conditionDt.getOr(DataType.UNDEFINED), valueDt.getOr(DataType.UNDEFINED), true)
|
||||
if(castedValue.first) {
|
||||
castedValue.second.linkParents(whenChoice)
|
||||
@@ -479,19 +343,68 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
}
|
||||
|
||||
override fun after(range: RangeExpression, parent: Node): Iterable<IAstModification> {
|
||||
val fromConst = range.from.constValue(program)
|
||||
val toConst = range.to.constValue(program)
|
||||
|
||||
if(fromConst!=null) {
|
||||
val smaller = NumericLiteral.optimalInteger(fromConst.number.toInt(), fromConst.position)
|
||||
if(fromConst.type.largerThan(smaller.type)) {
|
||||
val toType = range.to.inferType(program)
|
||||
if(toType isnot smaller.type) {
|
||||
if(toConst!=null) {
|
||||
// can we make the to value into the same smaller type?
|
||||
val smallerTo = NumericLiteral.optimalInteger(toConst.number.toInt(), toConst.position)
|
||||
if(smaller.type==smallerTo.type) {
|
||||
val newRange = RangeExpression(smaller, smallerTo, range.step, range.position)
|
||||
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val newRange = RangeExpression(smaller, range.to, range.step, range.position)
|
||||
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
if(toConst!=null) {
|
||||
val smaller = NumericLiteral.optimalInteger(toConst.number.toInt(), toConst.position)
|
||||
if(toConst.type.largerThan(smaller.type)) {
|
||||
val fromType = range.from.inferType(program)
|
||||
if(fromType isnot smaller.type) {
|
||||
if(fromConst!=null) {
|
||||
// can we make the from value into the same smaller type?
|
||||
val smallerFrom = NumericLiteral.optimalInteger(fromConst.number.toInt(), fromConst.position)
|
||||
if(smaller.type==smallerFrom.type) {
|
||||
val newRange = RangeExpression(smallerFrom, smaller, range.step, range.position)
|
||||
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val newRange = RangeExpression(range.from, smaller, range.step, range.position)
|
||||
return listOf(IAstModification.ReplaceNode(range, newRange, parent))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val fromDt = range.from.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val toDt = range.to.inferType(program).getOr(DataType.UNDEFINED)
|
||||
val modifications = mutableListOf<IAstModification>()
|
||||
val (commonDt, toChange) = BinaryExpression.commonDatatype(fromDt, toDt, range.from, range.to)
|
||||
if(toChange!=null)
|
||||
addTypecastOrCastedValueModification(modifications, toChange, commonDt, range)
|
||||
|
||||
return modifications
|
||||
}
|
||||
|
||||
override fun after(ifElse: IfElse, parent: Node): Iterable<IAstModification> {
|
||||
if(!options.strictBool && ifElse.condition.inferType(program).isBytes) {
|
||||
val cast = TypecastExpression(ifElse.condition, DataType.BOOL, false, ifElse.condition.position)
|
||||
return listOf(IAstModification.ReplaceNode(ifElse.condition, cast, ifElse))
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
val constIdx = arrayIndexedExpression.indexer.constIndex()
|
||||
if(constIdx!=null) {
|
||||
val smaller = NumericLiteral.optimalInteger(constIdx, arrayIndexedExpression.indexer.position)
|
||||
val idxDt = arrayIndexedExpression.indexer.indexExpr.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(idxDt.largerThan(smaller.type)) {
|
||||
val newIdx = ArrayIndex(smaller, smaller.position)
|
||||
val newIndexer = ArrayIndexedExpression(arrayIndexedExpression.arrayvar, newIdx, arrayIndexedExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(arrayIndexedExpression, newIndexer, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
@@ -505,18 +418,6 @@ class TypecastsAdder(val program: Program, val options: CompilationOptions, val
|
||||
val sourceDt = expressionToCast.inferType(program).getOr(DataType.UNDEFINED)
|
||||
if(sourceDt == requiredType)
|
||||
return
|
||||
if(!options.strictBool) {
|
||||
if(requiredType==DataType.BOOL && sourceDt in ByteDatatypes) {
|
||||
val cast = TypecastExpression(expressionToCast, DataType.BOOL, false, expressionToCast.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expressionToCast, cast, parent))
|
||||
return
|
||||
}
|
||||
if(requiredType in ByteDatatypes && sourceDt==DataType.BOOL) {
|
||||
val cast = TypecastExpression(expressionToCast, requiredType, false, expressionToCast.position)
|
||||
modifications.add(IAstModification.ReplaceNode(expressionToCast, cast, parent))
|
||||
return
|
||||
}
|
||||
}
|
||||
if(requiredType==DataType.BOOL) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -97,16 +97,6 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent))
|
||||
}
|
||||
|
||||
if(!options.strictBool && expr.operator=="not") {
|
||||
if(expr.expression.inferType(program).isBytes) {
|
||||
// not bytevalue --> bytevalue==0
|
||||
val cmp = BinaryExpression(expr.expression, "==",
|
||||
NumericLiteral(expr.expression.inferType(program).getOr(DataType.UNDEFINED), 0.0, expr.expression.position),
|
||||
expr.expression.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, cmp, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
@@ -145,9 +135,25 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
}
|
||||
|
||||
if(isMultiComparisonRecurse(leftBinExpr1)) {
|
||||
// replace it!
|
||||
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
|
||||
val elementType = needle.inferType(program).getOrElse { throw FatalAstException("invalid needle dt") }
|
||||
if(values.size==2 || values.size==3 && (elementType==DataType.UBYTE || elementType==DataType.UWORD)) {
|
||||
val numbers = values.map{it.number}.toSet()
|
||||
if(numbers == setOf(0.0, 1.0)) {
|
||||
// we can replace unsigned x==0 or x==1 with x<2
|
||||
val compare = BinaryExpression(needle, "<", NumericLiteral(elementType, 2.0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, compare, parent))
|
||||
}
|
||||
if(numbers == setOf(0.0, 1.0, 2.0)) {
|
||||
// we can replace unsigned x==0 or x==1 or x==2 with x<3
|
||||
val compare = BinaryExpression(needle, "<", NumericLiteral(elementType, 3.0, expr.position), expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, compare, parent))
|
||||
}
|
||||
}
|
||||
if(values.size<2)
|
||||
return noModifications
|
||||
|
||||
// replace x==1 or x==2 or x==3 with a containment check x in [1,2,3]
|
||||
val valueCopies = values.sortedBy { it.number }.map { it.copy() }
|
||||
val arrayType = ElementToArrayTypes.getValue(elementType)
|
||||
val valuesArray = ArrayLiteral(InferredTypes.InferredType.known(arrayType), valueCopies.toTypedArray(), expr.position)
|
||||
val containment = ContainmentCheck(needle, valuesArray, expr.position)
|
||||
|
||||
@@ -41,7 +41,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
private val memorySlabs = mutableListOf<Slab>()
|
||||
|
||||
override fun visit(functionCallExpr: FunctionCallExpression) {
|
||||
val error = checkTypes(functionCallExpr as IFunctionCall, program, options)
|
||||
val error = checkTypes(functionCallExpr as IFunctionCall, program)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
else {
|
||||
@@ -62,7 +62,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val error = checkTypes(functionCallStatement as IFunctionCall, program, options)
|
||||
val error = checkTypes(functionCallStatement as IFunctionCall, program)
|
||||
if(error!=null)
|
||||
errors.err(error.first, error.second)
|
||||
|
||||
@@ -85,7 +85,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
return false
|
||||
}
|
||||
|
||||
fun checkTypes(call: IFunctionCall, program: Program, options: CompilationOptions): Pair<String, Position>? {
|
||||
fun checkTypes(call: IFunctionCall, program: Program): Pair<String, Position>? {
|
||||
val argITypes = call.args.map { it.inferType(program) }
|
||||
val firstUnknownDt = argITypes.indexOfFirst { it.isUnknown }
|
||||
if(firstUnknownDt>=0) {
|
||||
@@ -105,12 +105,7 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch]
|
||||
val expected = consideredParamTypes[mismatch]
|
||||
return if(expected==DataType.BOOL && !options.strictBool && actual in ByteDatatypes)
|
||||
null
|
||||
else if(expected in ByteDatatypes && !options.strictBool && actual==DataType.BOOL)
|
||||
null
|
||||
else
|
||||
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
if(target.isAsmSubroutine) {
|
||||
if(target.asmReturnvaluesRegisters.size>1) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import com.github.michaelbull.result.getErrorOrElse
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.core.config.AbstractProjectConfig
|
||||
import io.kotest.core.spec.SpecExecutionOrder
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.fail
|
||||
import io.kotest.assertions.withClue
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.datatest.withData
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.target.*
|
||||
@@ -11,7 +12,6 @@ import prog8tests.helpers.*
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.readText
|
||||
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,6 @@ private fun compileTheThing(filepath: Path, optimize: Boolean, target: ICompilat
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
strictBool = true,
|
||||
symbolDefs = emptyMap(),
|
||||
outputDir = outputDir
|
||||
)
|
||||
@@ -90,13 +89,14 @@ class TestCompilerOnExamplesC64: FunSpec({
|
||||
listOf(false, true)
|
||||
)
|
||||
|
||||
onlyC64.forEach {
|
||||
val (source, optimize) = it
|
||||
val target = C64Target()
|
||||
val (displayName, filepath) = prepareTestFiles(source, optimize, target)
|
||||
test(displayName) {
|
||||
compileTheThing(filepath, optimize, target) shouldNotBe null
|
||||
}
|
||||
val target = C64Target()
|
||||
withData(
|
||||
nameFn = { it.second.first },
|
||||
onlyC64.map { it to prepareTestFiles(it.first, it.second, target) }
|
||||
) { (params, prep) ->
|
||||
val filepath = prep.second
|
||||
val optimize = params.second
|
||||
compileTheThing(filepath, optimize, target) shouldNotBe null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -130,6 +130,7 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
"highresbitmap",
|
||||
"kefrenbars",
|
||||
"keyboardhandler",
|
||||
"life",
|
||||
"mandelbrot",
|
||||
"multi-irq-old",
|
||||
"multi-irq-new",
|
||||
@@ -145,13 +146,14 @@ class TestCompilerOnExamplesCx16: FunSpec({
|
||||
listOf(false, true)
|
||||
)
|
||||
|
||||
onlyCx16.forEach {
|
||||
val (source, optimize) = it
|
||||
val target = Cx16Target()
|
||||
val (displayName, filepath) = prepareTestFiles(source, optimize, target)
|
||||
test(displayName) {
|
||||
compileTheThing(filepath, optimize, target) shouldNotBe null
|
||||
}
|
||||
val target = Cx16Target()
|
||||
withData(
|
||||
nameFn = { it.second.first },
|
||||
onlyCx16.map { it to prepareTestFiles(it.first, it.second, target) }
|
||||
) { (params, prep) ->
|
||||
val filepath = prep.second
|
||||
val optimize = params.second
|
||||
compileTheThing(filepath, optimize, target) shouldNotBe null
|
||||
}
|
||||
})
|
||||
|
||||
@@ -181,40 +183,39 @@ class TestCompilerOnExamplesBothC64andCx16: FunSpec({
|
||||
"tehtriz",
|
||||
"textelite",
|
||||
),
|
||||
listOf(false, true)
|
||||
listOf(false, true),
|
||||
listOf(C64Target(), Cx16Target())
|
||||
)
|
||||
|
||||
bothCx16AndC64.forEach {
|
||||
val (source, optimize) = it
|
||||
val c64target = C64Target()
|
||||
val cx16target = Cx16Target()
|
||||
val (displayNameC64, filepathC64) = prepareTestFiles(source, optimize, c64target)
|
||||
val (displayNameCx16, filepathCx16) = prepareTestFiles(source, optimize, cx16target)
|
||||
test(displayNameC64) {
|
||||
compileTheThing(filepathC64, optimize, c64target) shouldNotBe null
|
||||
}
|
||||
test(displayNameCx16) {
|
||||
compileTheThing(filepathCx16, optimize, cx16target) shouldNotBe null
|
||||
}
|
||||
withData(
|
||||
nameFn = { it.third.first },
|
||||
bothCx16AndC64.map { Triple(it.second, it.third, prepareTestFiles(it.first, it.second, it.third)) }
|
||||
) { params ->
|
||||
val filepath = params.third.second
|
||||
val optimize = params.first
|
||||
compileTheThing(filepath, optimize, params.second) shouldNotBe null
|
||||
}
|
||||
})
|
||||
|
||||
class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
|
||||
val onlyVirtual = listOf(
|
||||
val onlyVirtual = cartesianProduct(
|
||||
listOf(
|
||||
"bouncegfx",
|
||||
"bsieve",
|
||||
"pixelshader",
|
||||
"sincos"
|
||||
)
|
||||
),
|
||||
listOf(false, true)
|
||||
)
|
||||
|
||||
onlyVirtual.forEach {
|
||||
val target = VMTarget()
|
||||
val (displayName, filepath) = prepareTestFiles(it, false, target)
|
||||
test(displayName) {
|
||||
val src = filepath.readText()
|
||||
compileText(target, false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(target, true, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
val target = VMTarget()
|
||||
withData(
|
||||
nameFn = { it.second.first },
|
||||
onlyVirtual.map { it to prepareTestFiles(it.first, it.second, target) }
|
||||
) { (params, prep) ->
|
||||
val filepath = prep.second
|
||||
val optimize = params.second
|
||||
compileTheThing(filepath, optimize, target) shouldNotBe null
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.datatest.withData
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
@@ -93,26 +94,26 @@ class TestCompilerOnRanges: FunSpec({
|
||||
}
|
||||
|
||||
context("floatArrayInitializerWithRange") {
|
||||
val combos = cartesianProduct(
|
||||
listOf("", "42", "41"), // sizeInDecl
|
||||
listOf("%import floats", ""), // optEnableFloats
|
||||
listOf(Cx16Target(), C64Target()), // platform
|
||||
listOf(false, true) // optimize
|
||||
)
|
||||
|
||||
combos.forEach {
|
||||
val (sizeInDecl, optEnableFloats, platform, optimize) = it
|
||||
val displayName =
|
||||
when (sizeInDecl) {
|
||||
withData(
|
||||
nameFn = {
|
||||
when (it.first) {
|
||||
"" -> "no"
|
||||
"42" -> "correct"
|
||||
else -> "wrong"
|
||||
} + " array size given" +
|
||||
", " + (if (optEnableFloats == "") "without" else "with") + " %option enable_floats" +
|
||||
", ${platform.name}, optimize: $optimize"
|
||||
", " + (if (it.second == "") "without" else "with") + " %option enable_floats" +
|
||||
", ${it.third.name}, optimize: ${it.fourth}"
|
||||
},
|
||||
cartesianProduct(
|
||||
listOf("", "42", "41"), // sizeInDecl
|
||||
listOf("%import floats", ""), // optEnableFloats
|
||||
listOf(Cx16Target(), C64Target()), // platform
|
||||
listOf(false, true) // optimize
|
||||
)
|
||||
) { seq ->
|
||||
val (sizeInDecl, optEnableFloats, platform, optimize) = seq
|
||||
|
||||
test(displayName) {
|
||||
val result = compileText(platform, optimize, """
|
||||
val result = compileText(platform, optimize, """
|
||||
$optEnableFloats
|
||||
main {
|
||||
sub start() {
|
||||
@@ -121,12 +122,10 @@ class TestCompilerOnRanges: FunSpec({
|
||||
}
|
||||
}
|
||||
""")
|
||||
if (optEnableFloats != "" && (sizeInDecl=="" || sizeInDecl=="42"))
|
||||
result shouldNotBe null
|
||||
else
|
||||
result shouldBe null
|
||||
|
||||
}
|
||||
if (optEnableFloats != "" && (sizeInDecl=="" || sizeInDecl=="42"))
|
||||
result shouldNotBe null
|
||||
else
|
||||
result shouldBe null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
@@ -41,7 +41,6 @@ class TestCompilerOptionSourcedirs: FunSpec({
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
strictBool = true,
|
||||
symbolDefs = emptyMap(),
|
||||
sourceDirs,
|
||||
outputDir
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import com.github.michaelbull.result.expectError
|
||||
import com.github.michaelbull.result.getOrThrow
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.datatest.withData
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import prog8.ast.Module
|
||||
@@ -10,11 +12,8 @@ import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.PrefixExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.SourceCode
|
||||
import prog8.code.core.ZeropageWish
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.*
|
||||
import prog8tests.helpers.DummyFunctions
|
||||
import prog8tests.helpers.DummyMemsizer
|
||||
import prog8tests.helpers.DummyStringEncoder
|
||||
@@ -244,4 +243,34 @@ class TestMemory: FunSpec({
|
||||
}
|
||||
""", writeAssembly = true) shouldBe null
|
||||
}
|
||||
|
||||
context("memsizer") {
|
||||
withData(VMTarget(), AtariTarget(), C64Target(), PETTarget(), AtariTarget(), C128Target()) { target ->
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(DataType.UNDEFINED)
|
||||
}
|
||||
shouldThrow<IllegalArgumentException> {
|
||||
target.memorySize(DataType.LONG)
|
||||
}
|
||||
target.memorySize(DataType.BOOL) shouldBe 1
|
||||
target.memorySize(DataType.BYTE) shouldBe 1
|
||||
target.memorySize(DataType.WORD) shouldBe 2
|
||||
target.memorySize(DataType.FLOAT) shouldBe target.machine.FLOAT_MEM_SIZE
|
||||
target.memorySize(DataType.STR) shouldBe 2
|
||||
target.memorySize(DataType.ARRAY_UB) shouldBe 2
|
||||
target.memorySize(DataType.ARRAY_UW) shouldBe 2
|
||||
target.memorySize(DataType.ARRAY_F) shouldBe 2
|
||||
|
||||
shouldThrow<NoSuchElementException> {
|
||||
target.memorySize(DataType.UBYTE, 10)
|
||||
}
|
||||
target.memorySize(DataType.UWORD, 10) shouldBe 10 // uword is pointer to array of bytes
|
||||
target.memorySize(DataType.ARRAY_B, 10) shouldBe 10
|
||||
target.memorySize(DataType.ARRAY_UB, 10) shouldBe 10
|
||||
target.memorySize(DataType.ARRAY_F, 10) shouldBe 10*target.machine.FLOAT_MEM_SIZE
|
||||
target.memorySize(DataType.ARRAY_UW, 10) shouldBe 20
|
||||
target.memorySize(DataType.ARRAY_W_SPLIT, 10) shouldBe 20
|
||||
target.memorySize(DataType.ARRAY_UW_SPLIT, 10) shouldBe 20
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
@@ -523,21 +523,24 @@ main {
|
||||
xxValue.right shouldBe NumericLiteral(DataType.UWORD, 10.0, Position.DUMMY)
|
||||
}
|
||||
|
||||
test("multi-comparison replaced by containment check") {
|
||||
test("multi-comparison with many values replaced by containment check on heap variable") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared source=99
|
||||
ubyte @shared thingy=42
|
||||
|
||||
if source==3 or source==4 or source==99 or source==1
|
||||
if source==3 or source==4 or source==99 or source==1 or source==2 or source==42
|
||||
thingy++
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), optimize=true, src, writeAssembly=true) shouldNotBe null
|
||||
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
/*
|
||||
expected result:
|
||||
ubyte[] auto_heap_var = [1,4,99,3]
|
||||
ubyte[] auto_heap_var = [1,2,3,4,42,99]
|
||||
ubyte source
|
||||
source = 99
|
||||
ubyte thingy
|
||||
@@ -553,16 +556,43 @@ main {
|
||||
(containment.iterable as IdentifierReference).nameInSource.single() shouldStartWith "auto_heap_value"
|
||||
val arrayDecl = stmts[0] as VarDecl
|
||||
arrayDecl.isArray shouldBe true
|
||||
arrayDecl.arraysize?.constIndex() shouldBe 4
|
||||
arrayDecl.arraysize?.constIndex() shouldBe 6
|
||||
val arrayValue = arrayDecl.value as ArrayLiteral
|
||||
arrayValue.type shouldBe InferredTypes.InferredType.known(DataType.ARRAY_UB)
|
||||
arrayValue.value shouldBe listOf(
|
||||
NumericLiteral.optimalInteger(1, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(2, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(3, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(4, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(42, Position.DUMMY),
|
||||
NumericLiteral.optimalInteger(99, Position.DUMMY))
|
||||
}
|
||||
|
||||
test("multi-comparison with few values replaced by inline containment check") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared source=99
|
||||
ubyte @shared thingy=42
|
||||
|
||||
if source==3 or source==4 or source==99
|
||||
thingy++
|
||||
}
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), optimize=true, src, writeAssembly=true) shouldNotBe null
|
||||
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
val stmts = result.compilerAst.entrypoint.statements
|
||||
stmts.size shouldBe 5
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
val containment = ifStmt.condition as ContainmentCheck
|
||||
(containment.element as IdentifierReference).nameInSource shouldBe listOf("source")
|
||||
val array = (containment.iterable as ArrayLiteral)
|
||||
array.value.size shouldBe 3
|
||||
array.value.map { (it as NumericLiteral).number } shouldBe listOf(3.0, 4.0, 99.0)
|
||||
}
|
||||
|
||||
test("invalid multi-comparison (not all equals) not replaced") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import com.github.michaelbull.result.Ok
|
||||
import com.github.michaelbull.result.expectError
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.fail
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.ints.shouldBeGreaterThan
|
||||
@@ -839,4 +839,22 @@ main {
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("undefined symbol error instead of type cast error") {
|
||||
val src="""
|
||||
main {
|
||||
const ubyte foo = 0
|
||||
ubyte bar = 0
|
||||
sub start() {
|
||||
when foo {
|
||||
notdefined -> bar = 1
|
||||
else -> bar = 2
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[1] shouldContain "undefined symbol"
|
||||
}
|
||||
})
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import com.github.michaelbull.result.expectError
|
||||
import com.github.michaelbull.result.getOrElse
|
||||
|
||||
@@ -193,4 +193,48 @@ main {
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "statement"
|
||||
}
|
||||
|
||||
test("redefined variable name in single declaration is reported") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
const ubyte count=11
|
||||
cx16.r0++
|
||||
ubyte count = 88 ; redefinition
|
||||
cx16.r0 = count
|
||||
}
|
||||
}"""
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "name conflict"
|
||||
|
||||
errors.clear()
|
||||
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "name conflict"
|
||||
}
|
||||
|
||||
test("redefined variable name in multi declaration is reported") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte i
|
||||
i++
|
||||
ubyte i, j ; redefinition
|
||||
i++
|
||||
j++
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors=ErrorReporterForTests()
|
||||
compileText(C64Target(), false, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "name conflict"
|
||||
|
||||
errors.clear()
|
||||
compileText(C64Target(), true, src, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "name conflict"
|
||||
}
|
||||
})
|
||||
@@ -1046,4 +1046,14 @@ main {
|
||||
(assigns[3].value as NumericLiteral).number shouldBe 3000001.141592654
|
||||
}
|
||||
|
||||
test("oneliner") {
|
||||
val src="""
|
||||
main { sub start() { cx16.r0++ cx16.r1++ } }
|
||||
other { asmsub thing() { %asm {{ inx }} } }
|
||||
""";
|
||||
val result = compileText(VMTarget(), false, src, writeAssembly = false)!!
|
||||
val st = result.compilerAst.entrypoint.statements
|
||||
st.size shouldBe 2
|
||||
}
|
||||
|
||||
})
|
||||
@@ -10,8 +10,8 @@ import prog8.code.ast.PtAssignTarget
|
||||
import prog8.code.ast.PtAssignment
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.SourceCode
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.Cx16Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.parser.Prog8Parser.parseModule
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
@@ -136,7 +136,7 @@ main {
|
||||
}}
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
||||
errors.errors.size shouldBe 0
|
||||
@@ -186,7 +186,7 @@ main {
|
||||
romsub ${'$'}8003 = test3() -> uword @R1, bool @Pc, ubyte @X
|
||||
}"""
|
||||
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(C64Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(Cx16Target(), false, src, errors, true)!!
|
||||
errors.errors.size shouldBe 0
|
||||
|
||||
@@ -111,16 +111,23 @@ main {
|
||||
sub start() {
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, srcWrong1, writeAssembly = false) shouldBe null
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, srcWrong1, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "split can only be used on word arrays"
|
||||
|
||||
val srcWrong2 = """
|
||||
%option enable_floats
|
||||
main {
|
||||
float[10] @split sf
|
||||
|
||||
sub start() {
|
||||
}
|
||||
}"""
|
||||
compileText(C64Target(), false, srcWrong2, writeAssembly = false) shouldBe null
|
||||
errors.clear()
|
||||
compileText(C64Target(), false, srcWrong2, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "split can only be used on word arrays"
|
||||
}
|
||||
|
||||
test("split word arrays in asm as lsb/msb") {
|
||||
|
||||
@@ -383,4 +383,61 @@ main {
|
||||
compileText(AtariTarget(), false, text, writeAssembly = true) shouldNotBe null
|
||||
compileText(VMTarget(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("for loops with just 1 iteration") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
for cx16.r0L in 100 downto 100 {
|
||||
cx16.r1++
|
||||
}
|
||||
for cx16.r0L in 100 to 100 {
|
||||
cx16.r1++
|
||||
}
|
||||
for cx16.r0 in 2222 downto 2222 {
|
||||
cx16.r1++
|
||||
}
|
||||
for cx16.r0 in 2222 to 2222 {
|
||||
cx16.r1++
|
||||
}
|
||||
|
||||
for cx16.r0L in 100 downto 100 step -5 {
|
||||
cx16.r1++
|
||||
}
|
||||
for cx16.r0L in 100 to 100 step 5 {
|
||||
cx16.r1++
|
||||
}
|
||||
for cx16.r0 in 2222 downto 2222 step -5 {
|
||||
cx16.r1++
|
||||
}
|
||||
for cx16.r0 in 2222 to 2222 step 5 {
|
||||
cx16.r1++
|
||||
}
|
||||
}
|
||||
}"""
|
||||
compileText(VMTarget(), true, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(VMTarget(), false, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(Cx16Target(), true, src, writeAssembly = true) shouldNotBe null
|
||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
test("multiple status flags return values from asmsub") {
|
||||
val src="""
|
||||
main {
|
||||
romsub 5000 = carryAndNegativeAndByteAndWord() -> bool @Pc, bool @Pn, ubyte @X, uword @AY
|
||||
|
||||
sub start() {
|
||||
ubyte @shared x
|
||||
uword @shared w
|
||||
bool @shared flag1
|
||||
bool @shared flag2
|
||||
|
||||
flag1, flag2, x, w = carryAndNegativeAndByteAndWord()
|
||||
flag1, void, void, w = carryAndNegativeAndByteAndWord()
|
||||
void, void, void, void = carryAndNegativeAndByteAndWord()
|
||||
void carryAndNegativeAndByteAndWord()
|
||||
}
|
||||
}"""
|
||||
compileText(Cx16Target(), false, src, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
})
|
||||
@@ -43,7 +43,6 @@ internal fun compileFile(
|
||||
breakpointCpuInstruction = null,
|
||||
printAst1 = false,
|
||||
printAst2 = false,
|
||||
strictBool = true
|
||||
)
|
||||
return compileProgram(args)
|
||||
}
|
||||
|
||||
@@ -29,38 +29,3 @@ fun <T, U, V, W> cartesianProduct(
|
||||
yield(Product(a, b, c, d))
|
||||
}
|
||||
}
|
||||
|
||||
fun <A, B, R> mapCombinations(dim1: Iterable<A>, dim2: Iterable<B>, combine2: (A, B) -> R) =
|
||||
sequence {
|
||||
for (a in dim1)
|
||||
for (b in dim2)
|
||||
yield(combine2(a, b))
|
||||
}.toList()
|
||||
|
||||
fun <A, B, C, R> mapCombinations(
|
||||
dim1: Iterable<A>,
|
||||
dim2: Iterable<B>,
|
||||
dim3: Iterable<C>,
|
||||
combine3: (A, B, C) -> R
|
||||
) =
|
||||
sequence {
|
||||
for (a in dim1)
|
||||
for (b in dim2)
|
||||
for (c in dim3)
|
||||
yield(combine3(a, b, c))
|
||||
}.toList()
|
||||
|
||||
fun <A, B, C, D, R> mapCombinations(
|
||||
dim1: Iterable<A>,
|
||||
dim2: Iterable<B>,
|
||||
dim3: Iterable<C>,
|
||||
dim4: Iterable<D>,
|
||||
combine4: (A, B, C, D) -> R
|
||||
) =
|
||||
sequence {
|
||||
for (a in dim1)
|
||||
for (b in dim2)
|
||||
for (c in dim3)
|
||||
for (d in dim4)
|
||||
yield(combine4(a, b, c, d))
|
||||
}.toList()
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8tests
|
||||
package prog8tests.compiler
|
||||
|
||||
import io.kotest.assertions.throwables.shouldThrow
|
||||
import io.kotest.assertions.withClue
|
||||
|
||||
@@ -189,7 +189,7 @@ private fun Asmsub_declContext.toAst(): AsmsubDecl {
|
||||
val params = asmsub_params()?.toAst() ?: emptyList()
|
||||
val returns = asmsub_returns()?.toAst() ?: emptyList()
|
||||
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
|
||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.zp, it.position) }
|
||||
val normalReturntypes = returns.map { it.type }
|
||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
|
||||
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag) }
|
||||
@@ -200,7 +200,7 @@ private class AsmSubroutineParameter(name: String,
|
||||
type: DataType,
|
||||
val registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?,
|
||||
position: Position) : SubroutineParameter(name, type, position)
|
||||
position: Position) : SubroutineParameter(name, type, ZeropageWish.DONTCARE, position)
|
||||
|
||||
private class AsmSubroutineReturn(val type: DataType,
|
||||
val registerOrPair: RegisterOrPair?,
|
||||
@@ -310,6 +310,8 @@ private fun SubroutineContext.toAst() : Subroutine {
|
||||
|
||||
private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
vardecl().map {
|
||||
val options = it.decloptions()
|
||||
val zp = getZpOption(options)
|
||||
var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||
if(it.ARRAYSIG()!=null || it.arrayindex()!=null)
|
||||
datatype = ElementToArrayTypes.getValue(datatype)
|
||||
@@ -318,9 +320,20 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
if(identifiers.size>1)
|
||||
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
|
||||
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].VOID()
|
||||
SubroutineParameter(identifiername.text, datatype, it.toPosition())
|
||||
SubroutineParameter(identifiername.text, datatype, zp, it.toPosition())
|
||||
}
|
||||
|
||||
private fun getZpOption(options: DecloptionsContext?): ZeropageWish {
|
||||
if(options==null)
|
||||
return ZeropageWish.DONTCARE
|
||||
return when {
|
||||
options.ZEROPAGEREQUIRE().isNotEmpty() -> ZeropageWish.REQUIRE_ZEROPAGE
|
||||
options.ZEROPAGE().isNotEmpty() -> ZeropageWish.PREFER_ZEROPAGE
|
||||
options.ZEROPAGENOT().isNotEmpty() -> ZeropageWish.NOT_IN_ZEROPAGE
|
||||
else -> ZeropageWish.DONTCARE
|
||||
}
|
||||
}
|
||||
|
||||
private fun Assign_targetContext.toAst() : AssignTarget {
|
||||
return when(this) {
|
||||
is IdentifierTargetContext -> {
|
||||
@@ -671,12 +684,7 @@ private fun When_choiceContext.toAst(): WhenChoice {
|
||||
|
||||
private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl {
|
||||
val options = decloptions()
|
||||
val zp = when {
|
||||
options.ZEROPAGEREQUIRE().isNotEmpty() -> ZeropageWish.REQUIRE_ZEROPAGE
|
||||
options.ZEROPAGE().isNotEmpty() -> ZeropageWish.PREFER_ZEROPAGE
|
||||
options.ZEROPAGENOT().isNotEmpty() -> ZeropageWish.NOT_IN_ZEROPAGE
|
||||
else -> ZeropageWish.DONTCARE
|
||||
}
|
||||
val zp = getZpOption(options)
|
||||
val identifiers = identifier()
|
||||
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].VOID()
|
||||
val name = if(identifiers.size==1) identifiername.text else "<multiple>"
|
||||
|
||||
@@ -5,14 +5,13 @@ import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ExpressionError
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.UndefinedSymbolError
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.encodings.JapaneseCharacterConverter
|
||||
import java.io.CharConversionException
|
||||
import java.util.*
|
||||
import java.util.Objects
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.truncate
|
||||
@@ -498,6 +497,24 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
||||
fun fromBoolean(bool: Boolean, position: Position) =
|
||||
NumericLiteral(DataType.BOOL, if(bool) 1.0 else 0.0, position)
|
||||
|
||||
fun optimalNumeric(origType1: DataType, origType2: DataType?, value: Number, position: Position) : NumericLiteral {
|
||||
val optimal = optimalNumeric(value, position)
|
||||
val largestOrig = if(origType2==null) origType1 else if(origType1.largerThan(origType2)) origType1 else origType2
|
||||
if(largestOrig.largerThan(optimal.type))
|
||||
return NumericLiteral(largestOrig, optimal.number, position)
|
||||
else
|
||||
return optimal
|
||||
}
|
||||
|
||||
fun optimalInteger(origType1: DataType, origType2: DataType?, value: Int, position: Position): NumericLiteral {
|
||||
val optimal = optimalInteger(value, position)
|
||||
val largestOrig = if(origType2==null) origType1 else if(origType1.largerThan(origType2)) origType1 else origType2
|
||||
if(largestOrig.largerThan(optimal.type))
|
||||
return NumericLiteral(largestOrig, optimal.number, position)
|
||||
else
|
||||
return optimal
|
||||
}
|
||||
|
||||
fun optimalNumeric(value: Number, position: Position): NumericLiteral {
|
||||
val digits = floor(value.toDouble()) - value.toDouble()
|
||||
return if(value is Double && digits!=0.0) {
|
||||
@@ -1006,6 +1023,7 @@ class RangeExpression(var from: Expression,
|
||||
|
||||
fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
||||
return when {
|
||||
fromVal == toVal -> fromVal .. toVal
|
||||
fromVal <= toVal -> when {
|
||||
stepVal <= 0 -> IntRange.EMPTY
|
||||
stepVal == 1 -> fromVal..toVal
|
||||
@@ -1081,7 +1099,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
if(sub.parameters.any { it.name==nameInSource.last() })
|
||||
return null
|
||||
}
|
||||
throw UndefinedSymbolError(this)
|
||||
return null
|
||||
}
|
||||
val vardecl = node as? VarDecl
|
||||
if(vardecl==null) {
|
||||
@@ -1089,7 +1107,16 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
} else if(vardecl.type!= VarDeclType.CONST) {
|
||||
return null
|
||||
}
|
||||
return vardecl.value?.constValue(program)
|
||||
|
||||
// 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)
|
||||
return value
|
||||
val optimal = NumericLiteral.optimalNumeric(value.number, value.position)
|
||||
if(optimal.type==vardecl.datatype)
|
||||
return optimal
|
||||
return null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package prog8.ast.expressions
|
||||
|
||||
import prog8.code.core.*
|
||||
import java.util.*
|
||||
import java.util.Objects
|
||||
|
||||
|
||||
object InferredTypes {
|
||||
|
||||
@@ -236,7 +236,7 @@ class VarDecl(val type: VarDeclType,
|
||||
|
||||
fun fromParameter(param: SubroutineParameter): VarDecl {
|
||||
val dt = if(param.type in ArrayDatatypes) DataType.UWORD else param.type
|
||||
return VarDecl(VarDeclType.VAR, VarDeclOrigin.SUBROUTINEPARAM, dt, ZeropageWish.DONTCARE, null, param.name, emptyList(), null,
|
||||
return VarDecl(VarDeclType.VAR, VarDeclOrigin.SUBROUTINEPARAM, dt, param.zp, null, param.name, emptyList(), null,
|
||||
sharedWithAsm = false,
|
||||
splitArray = false,
|
||||
position = param.position
|
||||
@@ -813,6 +813,7 @@ class Subroutine(override val name: String,
|
||||
|
||||
open class SubroutineParameter(val name: String,
|
||||
val type: DataType,
|
||||
val zp: ZeropageWish,
|
||||
final override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
@@ -824,7 +825,7 @@ open class SubroutineParameter(val name: String,
|
||||
throw FatalAstException("can't replace anything in a subroutineparameter node")
|
||||
}
|
||||
|
||||
override fun copy() = SubroutineParameter(name, type, position)
|
||||
override fun copy() = SubroutineParameter(name, type, zp, position)
|
||||
override fun toString() = "Param($type:$name)"
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = nameInSource.size==1 && name==nameInSource[0]
|
||||
}
|
||||
|
||||
@@ -160,11 +160,22 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
fun unused(module: Module) = module !in usedModules
|
||||
|
||||
fun unused(sub: Subroutine): Boolean {
|
||||
return sub !in usedSubroutines && !nameInAssemblyCode(sub.name, listOf("p8s_", ""))
|
||||
if(sub in usedSubroutines)
|
||||
return false
|
||||
val asm = nameInAssemblyCode(sub.name, listOf("p8s_", ""))
|
||||
if(asm!=null) {
|
||||
// check that the asm block that calls us, is inside a subroutine that is unused or not
|
||||
val containingSub = asm.definingSubroutine
|
||||
return if(containingSub != null && containingSub !== sub)
|
||||
unused(containingSub)
|
||||
else
|
||||
false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fun unused(block: Block): Boolean {
|
||||
return block !in usedBlocks && !nameInAssemblyCode(block.name, listOf("p8b_", ""))
|
||||
return block !in usedBlocks && nameInAssemblyCode(block.name, listOf("p8b_", ""))==null
|
||||
}
|
||||
|
||||
fun unused(decl: VarDecl): Boolean {
|
||||
@@ -187,13 +198,13 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
||||
}
|
||||
|
||||
private val prefixes = listOf("p8b_", "p8v_", "p8s_", "p8c_", "p8l_", "p8_", "")
|
||||
private fun nameInAssemblyCode(name: String, knownAsmPrefixes: List<String> = emptyList()): Boolean {
|
||||
private fun nameInAssemblyCode(name: String, knownAsmPrefixes: List<String> = emptyList()): InlineAssembly? {
|
||||
if(knownAsmPrefixes.isNotEmpty())
|
||||
return allAssemblyNodes.any {
|
||||
return allAssemblyNodes.firstOrNull {
|
||||
knownAsmPrefixes.any { prefix -> prefix+name in it.names }
|
||||
}
|
||||
|
||||
return allAssemblyNodes.any {
|
||||
return allAssemblyNodes.firstOrNull {
|
||||
prefixes.any { prefix -> prefix+name in it.names }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.4.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-atari.p8
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.4.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c128.p8
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
Prog8 compiler v10.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.4.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-c64.p8
|
||||
Compiler target: c64
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: conv
|
||||
-------------------------
|
||||
conv {
|
||||
@@ -470,6 +469,9 @@ cx16 {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: shared_cbm_textio_functions
|
||||
------------------------------------------------
|
||||
|
||||
LIBRARY MODULE NAME: diskio
|
||||
---------------------------
|
||||
diskio {
|
||||
@@ -548,6 +550,9 @@ string {
|
||||
}
|
||||
|
||||
|
||||
LIBRARY MODULE NAME: shared_string_functions
|
||||
--------------------------------------------
|
||||
|
||||
LIBRARY MODULE NAME: floats
|
||||
---------------------------
|
||||
floats {
|
||||
@@ -581,6 +586,7 @@ floats {
|
||||
FREADS32 () -> clobbers (A,X,Y)
|
||||
FREADSA (byte value @A) -> clobbers (A,X,Y) = $bc3c
|
||||
FREADSTR (ubyte length @A) -> clobbers (A,X,Y) = $b7b5
|
||||
FREADU24AXY (ubyte lo @A, ubyte mid @X, ubyte hi @Y) -> clobbers (A,X,Y) -> float @FAC1
|
||||
FREADUS32 () -> clobbers (A,X,Y)
|
||||
FREADUY (ubyte value @Y) -> clobbers (A,X,Y) = $b3a2
|
||||
FSUB (uword mflpt @AY) -> clobbers (A,X,Y) = $b850
|
||||
@@ -635,6 +641,7 @@ floats {
|
||||
secant (float value) -> float
|
||||
sin (float angle) -> float
|
||||
tan (float value) -> float
|
||||
time () -> float @FAC1
|
||||
tostr (float value @FAC1) -> clobbers (X) -> str @AY
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.4.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-cx16.p8
|
||||
@@ -86,7 +86,7 @@ diskio {
|
||||
load_raw (uword filenameptr, uword startaddress) -> uword
|
||||
load_size (ubyte startbank, uword startaddress, uword endaddress) -> uword
|
||||
mkdir (str name)
|
||||
relabel (str name)
|
||||
relabel (str name)
|
||||
rename (uword oldfileptr, uword newfileptr)
|
||||
reset_read_channel ()
|
||||
reset_write_channel ()
|
||||
@@ -258,6 +258,7 @@ cx16 {
|
||||
const ubyte EXTAPI_getlfs
|
||||
const ubyte EXTAPI_iso_cursor_char
|
||||
const ubyte EXTAPI_joystick_ps2_keycodes
|
||||
const ubyte EXTAPI_kbd_leds
|
||||
const ubyte EXTAPI_led_update
|
||||
const ubyte EXTAPI_mouse_set_position
|
||||
const ubyte EXTAPI_mouse_sprite_offset
|
||||
@@ -566,6 +567,7 @@ cx16 {
|
||||
mouse_get (ubyte zdataptr @X) -> ubyte @A, byte @X = $ff6b
|
||||
mouse_get_sprite_offset () -> clobbers (A,X,Y) -> word @R0, word @R1
|
||||
mouse_pos () -> ubyte @A, uword @R0, uword @R1, byte @X
|
||||
mouse_present () -> bool
|
||||
mouse_scan () -> clobbers (A,X,Y) = $ff71
|
||||
mouse_set_pos (uword xpos @R0, uword ypos @R1) -> clobbers (X)
|
||||
mouse_set_sprite_offset (word xoffset @R0, word yoffset @R1) -> clobbers (A,X,Y)
|
||||
@@ -796,7 +798,9 @@ emudbg {
|
||||
console_value1 (ubyte value)
|
||||
console_value2 (ubyte value)
|
||||
console_write (str isoString)
|
||||
cpu_cycles () -> uword @R0, uword @R1
|
||||
is_emulator () -> bool
|
||||
reset_cpu_cycles ()
|
||||
}
|
||||
|
||||
|
||||
@@ -830,6 +834,7 @@ floats {
|
||||
FPWR (uword mflpt @AY) -> clobbers (A,X,Y) = $fe36
|
||||
FPWRT () -> clobbers (A,X,Y) = $fe39
|
||||
FREADSA (byte value @A) -> clobbers (A,X,Y)
|
||||
FREADU24AXY (ubyte lo @A, ubyte mid @X, ubyte hi @Y) -> clobbers (A,X,Y) -> float @FAC1
|
||||
FREADUY (ubyte value @Y)
|
||||
FSUB (uword mflpt @AY) -> clobbers (A,X,Y) = $fe12
|
||||
FSUBT () -> clobbers (A,X,Y) = $fe15
|
||||
@@ -888,6 +893,7 @@ floats {
|
||||
secant (float value) -> float
|
||||
sin (float angle) -> float
|
||||
tan (float value) -> float
|
||||
time () -> float @FAC1
|
||||
tostr (float value @FAC1) -> clobbers (X) -> str @AY
|
||||
}
|
||||
|
||||
@@ -945,9 +951,11 @@ graphics {
|
||||
disable_bitmap_mode ()
|
||||
disc (uword xcenter, ubyte ycenter, ubyte radius)
|
||||
enable_bitmap_mode ()
|
||||
filled_oval (uword xcenter, ubyte ycenter, uword h_radius, ubyte v_radius)
|
||||
fillrect (uword xx, uword yy, uword width, uword height)
|
||||
horizontal_line (uword xx, uword yy, uword length)
|
||||
line (uword x1, ubyte y1, uword x2, ubyte y2)
|
||||
oval (uword xcenter, ubyte ycenter, uword h_radius, ubyte v_radius)
|
||||
plot (uword plotx @R0, uword ploty @R1) -> clobbers (A,X,Y)
|
||||
rect (uword xx, uword yy, uword width, uword height)
|
||||
vertical_line (uword xx, uword yy, uword height)
|
||||
@@ -1002,8 +1010,8 @@ monogfx {
|
||||
ubyte mode
|
||||
uword width
|
||||
circle (uword xcenter, uword ycenter, ubyte radius, bool draw)
|
||||
clear_screen (ubyte color)
|
||||
cs_innerloop640 (ubyte color @A) -> clobbers (Y)
|
||||
clear_screen (bool draw)
|
||||
cs_innerloop640 (bool draw @A) -> clobbers (Y)
|
||||
disc (uword xcenter, uword ycenter, ubyte radius, bool draw)
|
||||
drawmode (ubyte dm)
|
||||
fill (uword x, uword y, bool draw)
|
||||
@@ -1104,6 +1112,8 @@ sprites {
|
||||
set_palette_offset (ubyte spritenum, ubyte offset)
|
||||
setx (ubyte spritenum, word xpos)
|
||||
sety (ubyte spritenum, word ypos)
|
||||
show (ubyte spritenum)
|
||||
zdepth (ubyte spritenum, ubyte depth)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.4.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-pet32.p8
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
Prog8 compiler v10.4 by Irmen de Jong (irmen@razorvine.net)
|
||||
Prog8 compiler v10.4.1 by Irmen de Jong (irmen@razorvine.net)
|
||||
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
|
||||
|
||||
Compiling program import-all-virtual.p8
|
||||
@@ -334,7 +334,7 @@ monogfx {
|
||||
ubyte mode
|
||||
uword width
|
||||
circle (uword xcenter, uword ycenter, ubyte radius, bool draw)
|
||||
clear_screen (ubyte color)
|
||||
clear_screen (bool draw)
|
||||
disc (uword xcenter, uword ycenter, ubyte radius, bool draw)
|
||||
drawmode (ubyte dm)
|
||||
fill (uword x, uword y, bool draw)
|
||||
|
||||
@@ -65,8 +65,12 @@ For normal use, the ``installDist`` task should suffice and after succesful comp
|
||||
but the actual prog8 compiler should run on all operating systems that provide a java runtime (version 11 or newer).
|
||||
If you do have trouble building or running the compiler on your operating system, please let me know!
|
||||
|
||||
To successfully build and debug in IDEA, you have to manually generate the Antlr-parser classes first.
|
||||
The easiest way to do this is the following:
|
||||
To successfully build and debug in IDEA, you have to do two things manually first:
|
||||
|
||||
1. you have to generate the buildversion file, do this with the shell command: ``gradle createVersionFile``
|
||||
2. manually generate the Antlr-parser classes first.
|
||||
|
||||
The easiest way to build the parser classes this is the following:
|
||||
|
||||
1. make sure you have the Antlr4 plugin installed in IDEA
|
||||
2. right click the grammar file Prog8ANTLR.g4 in the parser project, and choose "Generate Antlr Recognizer" from the menu.
|
||||
@@ -155,10 +159,6 @@ One or more .p8 module files
|
||||
Don't perform any code optimizations.
|
||||
Useful for debugging or faster compilation cycles.
|
||||
|
||||
``-nostrictbool``
|
||||
Relax the strict boolean type checks: bytes and booleans can be interchanged again without explicit type casts.
|
||||
*This option will likely disappear in a future prog8 version, so you may want to prepare for that in your code!*
|
||||
|
||||
``-optfloatx``
|
||||
Also optimize float expressions if optimizations are enabled.
|
||||
Warning: can increase program size significantly if a lot of floating point expressions are used.
|
||||
@@ -382,14 +382,6 @@ Also avoid using 64tass' built-in function or type names as symbols in your inli
|
||||
The 64tass manual contains `a list of those <https://tass64.sourceforge.net/#functions>`_.
|
||||
|
||||
|
||||
Community
|
||||
---------
|
||||
Most of the development on Prog8 and the use of it is currently centered around
|
||||
the `Commander X16 <https://www.commanderx16.com/>`_ retro computer.
|
||||
Their `Discord server <https://discord.gg/nS2PqEC>`_ contains a small channel
|
||||
dedicated to Prog8. Other than that, use the issue tracker on github.
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
|
||||
@@ -38,6 +38,15 @@ This is free software, as defined in the GNU GPL 3.0 (https://www.gnu.org/licens
|
||||
are excluded from this particular license: you can do with those *whatever you want*.
|
||||
This means, for instance, that you can use the Prog8 compiler to create commercial software as long as you only sell *the actual resulting program*.
|
||||
|
||||
Community
|
||||
^^^^^^^^^
|
||||
Most of the development on Prog8 and the use of it is currently centered around
|
||||
the `Commander X16 <https://www.commanderx16.com/>`_ retro computer.
|
||||
However, the other compiler targets are also still worked on, most notably the C64 target where it all started with!
|
||||
|
||||
The X16 `Discord server <https://discord.gg/nS2PqEC>`_ contains a small channel
|
||||
dedicated to Prog8. Besides that, you can use the issue tracker on github for questions or problems or contributions.
|
||||
|
||||
Want to buy me a coffee or a pizza perhaps?
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
This project was created over the last couple of years by dedicating thousands of hours of my free time to it, to make it the best I possibly can.
|
||||
|
||||
@@ -903,7 +903,7 @@ to see what's in there.
|
||||
gfx2 (cx16 only)
|
||||
-----------------
|
||||
Full-screen multicolor bitmap graphics routines, available on the Cx16 machine only.
|
||||
Same interface as monogfx, but for color screens. For 1 bpp monochrome screens, use monogfx.
|
||||
Same interface as monogfx, but for color screens. For 1 bpp monochrome screens: use monogfx.
|
||||
|
||||
- multiple full-screen bitmap color resolutions
|
||||
- clearing screen, switching screen mode, also back to text mode
|
||||
@@ -916,6 +916,15 @@ Read the `gfx2 source code <https://github.com/irmen/prog8/tree/master/compiler/
|
||||
to see what's in there.
|
||||
|
||||
|
||||
gfx_lores (cx16 only)
|
||||
----------------------
|
||||
Heavily optimized graphics routines for just the single screen mode: lores 320*240, 256c (8bpp)
|
||||
This is screen mode 1 from the gfx2 module (and also compatible with X16's basic screen mode 128).
|
||||
|
||||
Read the `gfx_lores source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/gfx_lores.p8>`_
|
||||
to see what's in there.
|
||||
|
||||
|
||||
palette (cx16 only)
|
||||
--------------------
|
||||
Available for the Cx16 target. Various routines to set the display color palette.
|
||||
|
||||
@@ -518,6 +518,12 @@ Loops
|
||||
-----
|
||||
|
||||
The *for*-loop is used to let a variable iterate over a range of values. Iteration is done in steps of 1, but you can change this.
|
||||
|
||||
.. sidebar::
|
||||
Optimization
|
||||
|
||||
Usually a loop in descending order downto 0 or 1, produces more efficient assembly code than the same loop in ascending order.
|
||||
|
||||
The loop variable must be declared separately as byte or word earlier, so that you can reuse it for multiple occasions.
|
||||
Iterating with a floating point variable is not supported. If you want to loop over a floating-point array, use a loop with an integer index variable instead.
|
||||
If the from value is already outside of the loop range, the whole for loop is skipped.
|
||||
|
||||
@@ -75,8 +75,8 @@ Directives
|
||||
during normal system operation.
|
||||
When the program exits, it simply returns to the BASIC ready prompt.
|
||||
- style ``full`` -- claim the whole ZP for variables for the program, overwriting everything,
|
||||
except the few addresses mentioned above that are used by the system's IRQ routine.
|
||||
Even though the default IRQ routine is still active, it is impossible to use most BASIC and Kernal ROM routines.
|
||||
except for a few addresses that are used by the system's IRQ handler.
|
||||
Even though that default IRQ handler is still active, it is impossible to use most BASIC and Kernal ROM routines.
|
||||
This includes many floating point operations and several utility routines that do I/O, such as ``print``.
|
||||
This option makes programs smaller and faster because even more variables can
|
||||
be stored in the ZP (which allows for more efficient assembly code).
|
||||
|
||||
Loaded 100 of 123 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user