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
This commit is contained in:
Irmen de Jong 2024-01-20 21:34:17 +01:00
parent 0f83dc6491
commit 87c46ba730
8 changed files with 90 additions and 14 deletions

View File

@ -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)

View File

@ -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<String>()
private val zeropage = options.compTarget.machine.zeropage
private val allocator = VariableAllocator(symbolTable, options, errors)
private val assemblyLines = mutableListOf<String>()
private val assembly = mutableListOf<String>()
private val breakpointLabels = mutableListOf<String>()
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<String>) {
// 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("<output-assemblycode>", 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) {

View File

@ -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 {

View File

@ -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 {

View File

@ -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)

View File

@ -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)
...

View File

@ -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]
}
}

View File

@ -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