mirror of
https://github.com/irmen/prog8.git
synced 2025-01-18 13:34:02 +00:00
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:
parent
0f83dc6491
commit
87c46ba730
@ -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
|
||||
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)
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
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)
|
||||
|
@ -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)
|
||||
|
||||
...
|
||||
|
||||
|
||||
|
@ -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]
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user