From 87c46ba730977e920f7804c28ab325a6552236b9 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 20 Jan 2024 21:34:17 +0100 Subject: [PATCH] check boolean array size mismatch. check for weird string assignment. check for X16 problematic cpu instructions rmb, smb, bbr, bbs. tweak number node equality wrt bool type --- codeCore/src/prog8/code/ast/AstExpressions.kt | 5 ++- .../src/prog8/codegen/cpu6502/AsmGen.kt | 44 ++++++++++++++----- .../compiler/astprocessing/AstChecker.kt | 25 +++++++++++ compiler/test/ast/TestVariousCompilerAst.kt | 15 +++++++ .../prog8/ast/expressions/AstExpressions.kt | 5 ++- docs/source/todo.rst | 5 +++ examples/test.p8 | 3 +- gradle.properties | 2 +- 8 files changed, 90 insertions(+), 14 deletions(-) diff --git a/codeCore/src/prog8/code/ast/AstExpressions.kt b/codeCore/src/prog8/code/ast/AstExpressions.kt index cc6503b36..a0a1ba4e6 100644 --- a/codeCore/src/prog8/code/ast/AstExpressions.kt +++ b/codeCore/src/prog8/code/ast/AstExpressions.kt @@ -250,7 +250,10 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre override fun equals(other: Any?): Boolean { if(other==null || other !is PtNumber) return false - return number==other.number + else if(type!=DataType.BOOL && other.type!=DataType.BOOL) + return number==other.number + else + return type==other.type && number==other.number } operator fun compareTo(other: PtNumber): Int = number.compareTo(other.number) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt index 45dbcd1b1..8d8e99f0e 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt @@ -7,6 +7,7 @@ import prog8.code.SymbolTable import prog8.code.SymbolTableMaker import prog8.code.ast.* import prog8.code.core.* +import prog8.code.target.Cx16Target import prog8.codegen.cpu6502.assignment.* import java.util.* import kotlin.io.path.Path @@ -223,7 +224,7 @@ class AsmGen6502Internal ( internal val loopEndLabels = ArrayDeque() private val zeropage = options.compTarget.machine.zeropage private val allocator = VariableAllocator(symbolTable, options, errors) - private val assemblyLines = mutableListOf() + private val assembly = mutableListOf() private val breakpointLabels = mutableListOf() private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage) private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this) @@ -235,7 +236,7 @@ class AsmGen6502Internal ( fun compileToAssembly(): IAssemblyProgram? { - assemblyLines.clear() + assembly.clear() loopEndLabels.clear() println("Generating assembly code... ") @@ -243,15 +244,22 @@ class AsmGen6502Internal ( if(errors.noErrors()) { val output = options.outputDir.resolve("${program.name}.asm") + val asmLines = assembly.asSequence().flatMapTo(mutableListOf()) { it.split('\n') } + if(options.compTarget.name==Cx16Target.NAME) { + scanInvalid65816instructions(asmLines) + if(!errors.noErrors()) { + errors.report() + return null + } + } if(options.optimize) { - val separateLines = assemblyLines.flatMapTo(mutableListOf()) { it.split('\n') } - assemblyLines.clear() - while(optimizeAssembly(separateLines, options.compTarget.machine, symbolTable)>0) { + while(optimizeAssembly(asmLines, options.compTarget.machine, symbolTable)>0) { // optimize the assembly source code } - output.writeLines(separateLines) + output.writeLines(asmLines) } else { - output.writeLines(assemblyLines) + // write the unmodified code + output.writeLines(assembly) } return AssemblyProgram(program.name, options.outputDir, options.compTarget) } else { @@ -260,6 +268,22 @@ class AsmGen6502Internal ( } } + private fun scanInvalid65816instructions(asmLines: MutableList) { + // The CommanderX16 ships with a WDC 65C02 CPU or a WDC 65816 CPU + // The latter is compatible with the 65C02 except for 4 instructions: RMB, SMB, BBS, BBR. + // We cannot set a different 6502 CPU target for the 64tass assembler, because we still need to support the STP and WAI instructions... + // so we have to scan for these instructions ourselves. + val invalid = Regex("""\s*((rmb\s|smb\s|bbs\s|bbr\s)|(rmb[0-7]|smb[0-7]|bbs[0-7]|bbr[0-7]))""", RegexOption.IGNORE_CASE) + for((index, line) in asmLines.withIndex()) { + if(line.length>=4 && invalid.matchesAt(line, 0)) { + errors.err( + "invalid assembly instruction used (not compatible with the 65816 CPU): ${line.trim()}", + Position("", index, 1, 1) + ) + } + } + } + internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu private var lastSourceLineNumber: Int = -1 @@ -283,9 +307,9 @@ class AsmGen6502Internal ( for (line in fragment.splitToSequence('\n')) { val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line // trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation - assemblyLines.add(trimmed) + assembly.add(trimmed) } - } else assemblyLines.add(fragment) + } else assembly.add(fragment) } fun asmSymbolName(regs: RegisterOrPair): String = @@ -1056,7 +1080,7 @@ $repeatLabel""") if(asm.isIR) throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly") else - assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n')) + assembly.add(asm.assembly.trimEnd().trimStart('\r', '\n')) } private fun translate(incbin: PtIncludeBinary) { diff --git a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt index 6e0410c0c..0c72892f2 100644 --- a/compiler/src/prog8/compiler/astprocessing/AstChecker.kt +++ b/compiler/src/prog8/compiler/astprocessing/AstChecker.kt @@ -1538,6 +1538,25 @@ internal class AstChecker(private val program: Program, when (targetDt) { DataType.STR -> return err("string value expected") + DataType.ARRAY_BOOL -> { + // value may be either a single byte, or a byte arraysize (of all constant values)\ + if(value.type istype targetDt) { + if(!checkArrayValues(value, targetDt)) + return false + val arraySpecSize = arrayspec.constIndex() + val arraySize = value.value.size + if(arraySpecSize!=null && arraySpecSize>0) { + if(arraySpecSize>256) + return err("boolean array length must be 1-256") + val expectedSize = arrayspec.constIndex() ?: return err("array size specifier must be constant integer value") + if (arraySize != expectedSize) + return err("initializer array size mismatch (expecting $expectedSize, got $arraySize)") + return true + } + return err("invalid boolean array size, must be 1-256") + } + return err("invalid boolean array initialization value ${value.type}, expected $targetDt") + } DataType.ARRAY_UB, DataType.ARRAY_B -> { // value may be either a single byte, or a byte arraysize (of all constant values), or a range if(value.type istype targetDt) { @@ -1687,6 +1706,9 @@ internal class AstChecker(private val program: Program, DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> { correct = array.all { it in -32768..32767 } } + DataType.ARRAY_BOOL -> { + correct = array.all { it==0 || it==1 } + } DataType.ARRAY_F -> correct = true else -> throw FatalAstException("invalid array type $type") } @@ -1730,6 +1752,9 @@ internal class AstChecker(private val program: Program, if((sourceDatatype== DataType.UWORD || sourceDatatype== DataType.WORD) && (targetDatatype== DataType.UBYTE || targetDatatype== DataType.BYTE)) { errors.err("cannot assign word to byte, use msb() or lsb()?", position) } + else if(sourceDatatype in IterableDatatypes && targetDatatype in ByteDatatypes) { + errors.err("cannot assign string or array to a byte", position) + } else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes) errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position) else { diff --git a/compiler/test/ast/TestVariousCompilerAst.kt b/compiler/test/ast/TestVariousCompilerAst.kt index 1c80ad239..cd7207dbf 100644 --- a/compiler/test/ast/TestVariousCompilerAst.kt +++ b/compiler/test/ast/TestVariousCompilerAst.kt @@ -58,6 +58,21 @@ main { compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null } + test("array init size mismatch error") { + val text=""" +main { + sub start() { + ubyte[10] uba = [1,2,3] + bool[10] bba = [true, false, true] + } +}""" + val errors = ErrorReporterForTests() + compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null + errors.errors.size shouldBe 2 + errors.errors[0] shouldContain "size mismatch" + errors.errors[1] shouldContain "size mismatch" + } + test("invalid && operator") { val text=""" main { diff --git a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt index 95b588e1c..b2475312b 100644 --- a/compilerAst/src/prog8/ast/expressions/AstExpressions.kt +++ b/compilerAst/src/prog8/ast/expressions/AstExpressions.kt @@ -558,7 +558,10 @@ class NumericLiteral(val type: DataType, // only numerical types allowed override fun equals(other: Any?): Boolean { if(other==null || other !is NumericLiteral) return false - return number==other.number + else if(type!=DataType.BOOL && other.type!=DataType.BOOL) + return number==other.number + else + return type==other.type && number==other.number } operator fun compareTo(other: NumericLiteral): Int = number.compareTo(other.number) diff --git a/docs/source/todo.rst b/docs/source/todo.rst index 3e56d9da1..341062536 100644 --- a/docs/source/todo.rst +++ b/docs/source/todo.rst @@ -1,6 +1,11 @@ TODO ==== +maze: if cell & UP!=0 and @(celladdr(cx,cy-1)) & (WALKED|BACKTRACKED) ==0 + ^^ adding this !=0 caused a weird beq + / lda #1 / + to appear in front of the shortcircuit beq... + +make the breakpoint instruction selectable (BRK vs STP) + ... diff --git a/examples/test.p8 b/examples/test.p8 index 4e5252bf2..34c44fc40 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,8 +1,9 @@ -%import textio %zeropage basicsafe %option no_sysinit main { sub start() { + ubyte[10] uba = [1,2,3] + bool[10] bba = [true, false, true] } } diff --git a/gradle.properties b/gradle.properties index be1056b30..c54bfd40c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,4 +5,4 @@ org.gradle.daemon=true kotlin.code.style=official javaVersion=11 kotlinVersion=1.9.22 -version=10.0 +version=10.1-SNAPSHOT