mirror of
https://github.com/irmen/prog8.git
synced 2024-10-24 08:24:25 +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 {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is PtNumber)
|
if(other==null || other !is PtNumber)
|
||||||
return false
|
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)
|
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.SymbolTableMaker
|
||||||
import prog8.code.ast.*
|
import prog8.code.ast.*
|
||||||
import prog8.code.core.*
|
import prog8.code.core.*
|
||||||
|
import prog8.code.target.Cx16Target
|
||||||
import prog8.codegen.cpu6502.assignment.*
|
import prog8.codegen.cpu6502.assignment.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
@ -223,7 +224,7 @@ class AsmGen6502Internal (
|
|||||||
internal val loopEndLabels = ArrayDeque<String>()
|
internal val loopEndLabels = ArrayDeque<String>()
|
||||||
private val zeropage = options.compTarget.machine.zeropage
|
private val zeropage = options.compTarget.machine.zeropage
|
||||||
private val allocator = VariableAllocator(symbolTable, options, errors)
|
private val allocator = VariableAllocator(symbolTable, options, errors)
|
||||||
private val assemblyLines = mutableListOf<String>()
|
private val assembly = mutableListOf<String>()
|
||||||
private val breakpointLabels = mutableListOf<String>()
|
private val breakpointLabels = mutableListOf<String>()
|
||||||
private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage)
|
private val forloopsAsmGen = ForLoopsAsmGen(this, zeropage)
|
||||||
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
private val postincrdecrAsmGen = PostIncrDecrAsmGen(program, this)
|
||||||
@ -235,7 +236,7 @@ class AsmGen6502Internal (
|
|||||||
|
|
||||||
fun compileToAssembly(): IAssemblyProgram? {
|
fun compileToAssembly(): IAssemblyProgram? {
|
||||||
|
|
||||||
assemblyLines.clear()
|
assembly.clear()
|
||||||
loopEndLabels.clear()
|
loopEndLabels.clear()
|
||||||
|
|
||||||
println("Generating assembly code... ")
|
println("Generating assembly code... ")
|
||||||
@ -243,15 +244,22 @@ class AsmGen6502Internal (
|
|||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
val output = options.outputDir.resolve("${program.name}.asm")
|
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) {
|
if(options.optimize) {
|
||||||
val separateLines = assemblyLines.flatMapTo(mutableListOf()) { it.split('\n') }
|
while(optimizeAssembly(asmLines, options.compTarget.machine, symbolTable)>0) {
|
||||||
assemblyLines.clear()
|
|
||||||
while(optimizeAssembly(separateLines, options.compTarget.machine, symbolTable)>0) {
|
|
||||||
// optimize the assembly source code
|
// optimize the assembly source code
|
||||||
}
|
}
|
||||||
output.writeLines(separateLines)
|
output.writeLines(asmLines)
|
||||||
} else {
|
} else {
|
||||||
output.writeLines(assemblyLines)
|
// write the unmodified code
|
||||||
|
output.writeLines(assembly)
|
||||||
}
|
}
|
||||||
return AssemblyProgram(program.name, options.outputDir, options.compTarget)
|
return AssemblyProgram(program.name, options.outputDir, options.compTarget)
|
||||||
} else {
|
} 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
|
internal fun isTargetCpu(cpu: CpuType) = options.compTarget.machine.cpu == cpu
|
||||||
|
|
||||||
private var lastSourceLineNumber: Int = -1
|
private var lastSourceLineNumber: Int = -1
|
||||||
@ -283,9 +307,9 @@ class AsmGen6502Internal (
|
|||||||
for (line in fragment.splitToSequence('\n')) {
|
for (line in fragment.splitToSequence('\n')) {
|
||||||
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
||||||
// trimmed = trimmed.replace(Regex("^\\+\\s+"), "+\t") // sanitize local label indentation
|
// 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 =
|
fun asmSymbolName(regs: RegisterOrPair): String =
|
||||||
@ -1056,7 +1080,7 @@ $repeatLabel""")
|
|||||||
if(asm.isIR)
|
if(asm.isIR)
|
||||||
throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly")
|
throw AssemblyError("%asm containing IR code cannot be translated to 6502 assembly")
|
||||||
else
|
else
|
||||||
assemblyLines.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
assembly.add(asm.assembly.trimEnd().trimStart('\r', '\n'))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(incbin: PtIncludeBinary) {
|
private fun translate(incbin: PtIncludeBinary) {
|
||||||
|
@ -1538,6 +1538,25 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
when (targetDt) {
|
when (targetDt) {
|
||||||
DataType.STR -> return err("string value expected")
|
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 -> {
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
|
// value may be either a single byte, or a byte arraysize (of all constant values), or a range
|
||||||
if(value.type istype targetDt) {
|
if(value.type istype targetDt) {
|
||||||
@ -1687,6 +1706,9 @@ internal class AstChecker(private val program: Program,
|
|||||||
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> {
|
DataType.ARRAY_W, DataType.ARRAY_W_SPLIT -> {
|
||||||
correct = array.all { it in -32768..32767 }
|
correct = array.all { it in -32768..32767 }
|
||||||
}
|
}
|
||||||
|
DataType.ARRAY_BOOL -> {
|
||||||
|
correct = array.all { it==0 || it==1 }
|
||||||
|
}
|
||||||
DataType.ARRAY_F -> correct = true
|
DataType.ARRAY_F -> correct = true
|
||||||
else -> throw FatalAstException("invalid array type $type")
|
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)) {
|
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)
|
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)
|
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)
|
errors.err("cannot assign float to ${targetDatatype.name.lowercase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position)
|
||||||
else {
|
else {
|
||||||
|
@ -58,6 +58,21 @@ main {
|
|||||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
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") {
|
test("invalid && operator") {
|
||||||
val text="""
|
val text="""
|
||||||
main {
|
main {
|
||||||
|
@ -558,7 +558,10 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
|
|||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if(other==null || other !is NumericLiteral)
|
if(other==null || other !is NumericLiteral)
|
||||||
return false
|
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)
|
operator fun compareTo(other: NumericLiteral): Int = number.compareTo(other.number)
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
TODO
|
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
|
%zeropage basicsafe
|
||||||
%option no_sysinit
|
%option no_sysinit
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
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
|
kotlin.code.style=official
|
||||||
javaVersion=11
|
javaVersion=11
|
||||||
kotlinVersion=1.9.22
|
kotlinVersion=1.9.22
|
||||||
version=10.0
|
version=10.1-SNAPSHOT
|
||||||
|
Loading…
Reference in New Issue
Block a user