code check cleanups

This commit is contained in:
Irmen de Jong 2024-11-24 16:13:13 +01:00
parent 5c6bd9c091
commit 2e35f3c3a3
25 changed files with 70 additions and 72 deletions

View File

@ -227,8 +227,8 @@ class PtContainmentCheck(position: Position): PtExpression(DataType.BOOL, positi
get() = children[1] as? PtArray
companion object {
val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
const val MAX_SIZE_FOR_INLINE_CHECKS_BYTE = 5
const val MAX_SIZE_FOR_INLINE_CHECKS_WORD = 4
}
}

View File

@ -7,7 +7,7 @@ enum class DataType {
WORD, // pass by value 16 bits signed
LONG, // pass by value 32 bits signed
FLOAT, // pass by value machine dependent
BOOL, // pass by value bit 0 of a 8 bit byte
BOOL, // pass by value bit 0 of an 8-bit byte
STR, // pass by reference
ARRAY_UB, // pass by reference
ARRAY_B, // pass by reference

View File

@ -94,7 +94,7 @@ abstract class Zeropage(options: CompilationOptions): MemoryAllocator(options) {
}
synchronized(this) {
if(free.size > 0) {
if(free.isNotEmpty()) {
if(size==1) {
for(candidate in free.minOrNull()!! .. free.maxOrNull()!!+1u) {
if(oneSeparateByteFree(candidate))

View File

@ -6,7 +6,7 @@ import prog8.code.core.Zeropage
import prog8.code.core.ZeropageType
// reference: "Mapping the C128" zero page chapter.
// reference: "Mapping the C128" zeropage chapter.
class C128Zeropage(options: CompilationOptions) : Zeropage(options) {

View File

@ -81,7 +81,7 @@ class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
// The base addres is $04. Unfortunately it cannot be the same as on the Commander X16 ($02).
for(reg in 0..15) {

View File

@ -54,7 +54,7 @@ class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) {
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15

View File

@ -7,7 +7,7 @@ import java.io.CharConversionException
object PetsciiEncoding {
// decoding: from Petscii/Screencodes (0-255) to unicode
// decoding: from Petscii/Screencodes (0-255) to Unicode
// character tables used from https://github.com/irmen/cbmcodecs2
private val decodingPetsciiLowercase = charArrayOf(
@ -1157,16 +1157,16 @@ object PetsciiEncoding {
}
}
fun petscii2scr(petscii_code: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
fun petscii2scr(petsciicode: UByte, inverseVideo: Boolean): Result<UByte, CharConversionException> {
val code: UInt = when {
petscii_code <= 0x1fu -> petscii_code + 128u
petscii_code <= 0x3fu -> petscii_code.toUInt()
petscii_code <= 0x5fu -> petscii_code - 64u
petscii_code <= 0x7fu -> petscii_code - 32u
petscii_code <= 0x9fu -> petscii_code + 64u
petscii_code <= 0xbfu -> petscii_code - 64u
petscii_code <= 0xfeu -> petscii_code - 128u
petscii_code == 255.toUByte() -> 95u
petsciicode <= 0x1fu -> petsciicode + 128u
petsciicode <= 0x3fu -> petsciicode.toUInt()
petsciicode <= 0x5fu -> petsciicode - 64u
petsciicode <= 0x7fu -> petsciicode - 32u
petsciicode <= 0x9fu -> petsciicode + 64u
petsciicode <= 0xbfu -> petsciicode - 64u
petsciicode <= 0xfeu -> petsciicode - 128u
petsciicode == 255.toUByte() -> 95u
else -> return Err(CharConversionException("petscii code out of range"))
}
if(inverseVideo) {

View File

@ -34,7 +34,7 @@ class Neo6502Zeropage(options: CompilationOptions) : Zeropage(options) {
override fun allocateCx16VirtualRegisters() {
// Note: the 16 virtual registers R0-R15 are not regular allocated variables, they're *memory mapped* elsewhere to fixed addresses.
// However, to be able for the compiler to "see" them as zero page variables, we have to register them here as well.
// However, to be able for the compiler to "see" them as zeropage variables, we have to register them here as well.
// This is important because the compiler sometimes treats ZP variables more efficiently (for example if it's a pointer)
for(reg in 0..15) {
allocatedVariables["cx16.r${reg}"] = VarAllocation((2+reg*2).toUInt(), DataType.UWORD, 2) // cx16.r0 .. cx16.r15

View File

@ -10,7 +10,7 @@ import java.nio.file.Path
internal class AssemblyProgram(
override val name: String,
private val outputDir: Path,
outputDir: Path,
private val compTarget: ICompilationTarget) : IAssemblyProgram {
private val assemblyFile = outputDir.resolve("$name.asm")

View File

@ -119,8 +119,8 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
private fun funcDivmodW(fcall: PtBuiltinFunctionCall) {
asmgen.assignWordOperandsToAYAndVar(fcall.args[1], fcall.args[0], "P8ZP_SCRATCH_W1")
// math.divmod_uw_asm: -- divide two unsigned words (16 bit each) into 16 bit results
// input: P8ZP_SCRATCH_W1 in ZP: 16 bit number, A/Y: 16 bit divisor
// output: P8ZP_SCRATCH_W2 in ZP: 16 bit remainder, A/Y: 16 bit division result
// input: P8ZP_SCRATCH_W1 in ZP: 16-bit number, A/Y: 16 bit divisor
// output: P8ZP_SCRATCH_W2 in ZP: 16-bit remainder, A/Y: 16 bit division result
asmgen.out(" jsr prog8_math.divmod_uw_asm")
val var2name = asmgen.asmVariableName(fcall.args[2] as PtIdentifier)
val divisionTarget = AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UBYTE, fcall.definingISub(), fcall.args[2].position, var2name)

View File

@ -151,7 +151,7 @@ class IRCodeGen(
sub.chunks.removeAt(0)
sub.chunks.add(0, replacement)
} else if(first.label != sub.label) {
val next = if(first is IRCodeChunk) first else null
val next = first as? IRCodeChunk
sub.chunks.add(0, IRCodeChunk(sub.label, next))
}
}

View File

@ -84,7 +84,7 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
/*
Empty Code chunk with label ->
If next chunk has no label -> move label to next chunk, remove original
If next chunk has label -> label name should be the same, remove original. Otherwise merge both labels into 1.
If next chunk has label -> label name should be the same, remove original, otherwise merge both labels into 1.
If is last chunk -> keep chunk in place because of the label.
Empty Code chunk without label ->
should not have been generated! ERROR.
@ -195,10 +195,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
chunks += if(label!=null)
IRInlineAsmChunk(label, candidate.assembly, candidate.isIR, candidate.next)
else
chunks += candidate
candidate
}
}
is IRInlineBinaryChunk -> {
@ -206,10 +206,10 @@ class IRPeepholeOptimizer(private val irprog: IRProgram) {
chunks += candidate
else if(lastChunk.isEmpty()) {
val label = lastChunk.label
if(label!=null)
chunks += IRInlineBinaryChunk(label, candidate.data, candidate.next)
chunks += if(label!=null)
IRInlineBinaryChunk(label, candidate.data, candidate.next)
else
chunks += candidate
candidate
}
}
}

View File

@ -88,7 +88,7 @@ class VarConstantValueTypeAdjuster(
IAstModification.Remove(singleAssignment, singleAssignment.parent as IStatementContainer)
)
}
// variable only has a single write and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
// variable only has a single write, and it is the initialization value, so it can be replaced with a constant, IF the value is a constant
errors.info("variable '${decl.name}' is never written to and was replaced by a constant", decl.position)
val const = VarDecl(VarDeclType.CONST, decl.origin, decl.datatype, decl.zeropage, decl.arraysize, decl.name, decl.names, singleAssignment.value, decl.sharedWithAsm, decl.splitArray, decl.alignment, decl.dirty, decl.position)
return listOf(

View File

@ -177,7 +177,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
} catch (ac: ErrorsReportedException) {
if(args.printAst1 && resultingProgram!=null) {
println("\n*********** COMPILER AST *************")
printProgram(resultingProgram!!)
printProgram(resultingProgram)
println("*********** COMPILER AST END *************\n")
}
if (args.printAst2) {
@ -185,7 +185,7 @@ fun compileProgram(args: CompilerArguments): CompilationResult? {
println("There is no intermediate AST available because of compilation errors.")
else {
println("\n*********** INTERMEDIATE AST *************")
printAst(ast!!, true, ::println)
printAst(ast, true, ::println)
println("*********** INTERMEDIATE AST END *************\n")
}
}
@ -449,7 +449,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
}
removeUnusedCode(program, errors,compilerOptions)
for(num_cycles in 0..10000) {
for(numCycles in 0..10000) {
// keep optimizing expressions and statements until no more steps remain
val optsDone1 = program.simplifyExpressions(errors)
val optsDone2 = program.optimizeStatements(errors, functions, compilerOptions)
@ -463,7 +463,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
if (numOpts == 0)
break
if(num_cycles==10000) {
if(numCycles==10000) {
throw InternalCompilerException("optimizeAst() is looping endlessly, numOpts = $numOpts")
}
}

View File

@ -138,7 +138,7 @@ internal class AstChecker(private val program: Program,
} else if(valueDt.isIterable && expectedReturnValues[0]==DataType.UWORD) {
// you can return a string or array when an uword (pointer) is returned
} else if(valueDt istype DataType.UWORD && expectedReturnValues[0]==DataType.STR) {
// you can return a uword pointer when the return type is a string
// you can return an uword pointer when the return type is a string
} else {
errors.err("type $valueDt of return value doesn't match subroutine's return type ${expectedReturnValues[0]}",returnStmt.value!!.position)
}
@ -340,7 +340,7 @@ internal class AstChecker(private val program: Program,
val jumpTarget = jump.identifier?.targetStatement(program)
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = if(jumpTarget is Subroutine) jumpTarget else jumpTarget.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine
if(sub !== targetSub)
count++
}
@ -1173,7 +1173,7 @@ internal class AstChecker(private val program: Program,
val jumpTarget = jump.identifier?.targetStatement(program)
if(jumpTarget!=null) {
val sub = jump.definingSubroutine
val targetSub = if(jumpTarget is Subroutine) jumpTarget else jumpTarget.definingSubroutine
val targetSub = jumpTarget as? Subroutine ?: jumpTarget.definingSubroutine
if(sub !== targetSub)
count++
}
@ -1978,7 +1978,7 @@ internal class AstChecker(private val program: Program,
// this is allowed: bitwise operation between different types as long as they're the same size.
}
else if(targetDatatype==DataType.UWORD && sourceDatatype in PassByReferenceDatatypes) {
// this is allowed: a pass-by-reference datatype into a uword (pointer value).
// this is allowed: a pass-by-reference datatype into an uword (pointer value).
}
else if(sourceDatatype in ArrayDatatypes && targetDatatype in ArrayDatatypes) {
// this is allowed (assigning array to array)

View File

@ -2,7 +2,6 @@ package prog8.compiler.astprocessing
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.IfElse
import prog8.ast.walk.AstWalker

View File

@ -1,7 +1,6 @@
package prog8.compiler.astprocessing
import prog8.ast.IFunctionCall
import prog8.ast.INameScope
import prog8.ast.Node
import prog8.ast.Program
import prog8.ast.base.FatalAstException

View File

@ -422,10 +422,10 @@ private fun AugassignmentContext.toAst(): Assignment {
}
private fun DatatypeContext.toAst(): DataType {
try {
return DataType.valueOf(text.uppercase())
return try {
DataType.valueOf(text.uppercase())
} catch (_: IllegalArgumentException) {
return DataType.UNDEFINED
DataType.UNDEFINED
}
}

View File

@ -258,10 +258,10 @@ class BinaryExpression(
return when (leftDt) {
DataType.BOOL -> {
if(rightDt==DataType.BOOL)
return Pair(DataType.BOOL, null)
return if(rightDt==DataType.BOOL)
Pair(DataType.BOOL, null)
else
return Pair(DataType.BOOL, right)
Pair(DataType.BOOL, right)
}
DataType.UBYTE -> {
when (rightDt) {
@ -518,19 +518,19 @@ class NumericLiteral(val type: DataType, // only numerical types allowed
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)
return if(largestOrig.largerThan(optimal.type))
NumericLiteral(largestOrig, optimal.number, position)
else
return optimal
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)
return if(largestOrig.largerThan(optimal.type))
NumericLiteral(largestOrig, optimal.number, position)
else
return optimal
optimal
}
fun optimalNumeric(value: Number, position: Position): NumericLiteral {
@ -805,14 +805,14 @@ class CharLiteral private constructor(val value: Char,
companion object {
fun create(character: Char, encoding: Encoding, position: Position): CharLiteral {
if(encoding==Encoding.KATAKANA) {
return if(encoding==Encoding.KATAKANA) {
val processed = JapaneseCharacterConverter.zenkakuKatakanaToHankakuKatakana(character.toString())
if(processed.length==1)
return CharLiteral(processed[0], encoding, position)
CharLiteral(processed[0], encoding, position)
else
throw CharConversionException("character literal encodes into multiple bytes at $position")
} else
return CharLiteral(character, encoding, position)
CharLiteral(character, encoding, position)
}
fun fromEscaped(raw: String, encoding: Encoding, position: Position): CharLiteral {

View File

@ -12,7 +12,7 @@
Theoretical optimal chunk size is 512 bytes but actual size may be different. (In practice there seems to be no significant speed impact)
MCF files are meant to be be created using a tool on PC, and only being read on the X16.
MCF files are meant to be created using a tool on PC, and only being read on the X16.
A Python tool is provided to create a demo MCF file.
A proof of concept Prog8 library module and example program is provided to consume that demo MCF file on the X16.
@ -96,4 +96,4 @@ The second routine has the following signature:
These routines are provided to the streaming routine as callback addresses (ram bank number + address to call).
If any of these routines returns Carry set (error status) the streaming routine halts, otherwise it keeps on going.
The streaming continues until a End of File chunk type is encountered in the loadlist.
The streaming continues until an End of File chunk type is encountered in the loadlist.

View File

@ -1,3 +1,3 @@
You'll need to put the titlescreen data files from the 'musicdemo' project into this directory.
The musicdemo is on github: https://github.com/irmen/cx16musicdemo
The musicdemo is on GitHub: https://github.com/irmen/cx16musicdemo
The four files are the two ME- and the two DS- TITLESCREEN.BIN and .PAL files, and are generated by running the makefile in that project.

View File

@ -12,7 +12,7 @@ It then uses the streaming support in zsmkit to load the song from disk as it is
It shows some other features such as callbacks and pausing as well.
The way the zsmkit blob is embedded into the program is done by telling prog8 that
the 'main' block of the program has to start at $0830, and the very first command
in that block is not the usual 'start' subroutine but a %asmbinary command to load
in that block is not the usual 'start' subroutine but an %asmbinary command to load
and embed the zsmkit blob right there and then.
@ -29,7 +29,7 @@ this one was configured without streaming support enabled.
CUSTOMIZING ZSMKIT
------------------
Read the README on the zsmkit github repo. It contains a lot of important information,
Read the README on the zsmkit GitHub repo. It contains a lot of important information,
about how zsmkit works, but also about the various things you have to configure to build a
new library blob to your liking. The example here includes two recently built variants of the blob,
so you don't immediately have to build something yourself, but if you want to enable or disable

View File

@ -408,10 +408,10 @@ class IRFileReader {
skipText(reader)
while(reader.peek().isStartElement) {
when(reader.peek().asStartElement().name.localPart) {
"CODE" -> sub += parseCodeChunk(reader)
"BYTES" -> sub += parseBinaryBytes(reader)
"ASM" -> sub += parseInlineAssembly(reader)
sub += when(reader.peek().asStartElement().name.localPart) {
"CODE" -> parseCodeChunk(reader)
"BYTES" -> parseBinaryBytes(reader)
"ASM" -> parseInlineAssembly(reader)
else -> throw IRParseException("invalid line in SUB: ${reader.peek()}")
}
skipText(reader)

View File

@ -236,7 +236,7 @@ class IRProgram(val name: String,
i++
instr2 = chunk.instructions[i]
}
// it could be that the actual call is only in another code chunk, so IF we find one, we can check. Otherwise just skip the check...
// it could be that the actual call is only in another code chunk, so IF we find one, we can check, otherwise just skip the check...
if(chunk.instructions[i].fcallArgs!=null) {
val expectedRegisterLoads = chunk.instructions[i].fcallArgs!!.arguments.map { it.reg.registerNum }
require(registers.containsAll(expectedRegisterLoads)) { "not all argument registers are given a value in the preparecall-call sequence" }

View File

@ -568,7 +568,7 @@ object SysCalls {
return returnValue(callspec.returns.single(), height*256 + width, vm)
}
} catch (x: Exception) {
// dunno what happened...
// don't know what happened...
}
return returnValue(callspec.returns.single(), 30*256 + 80, vm) // just return some defaults in this case 80*30
}
@ -603,17 +603,17 @@ object SysCalls {
}
Syscall.READ_FILE_BYTE -> {
val (success, byte) = vm.read_file_byte()
if(success)
return returnValue(callspec.returns.single(), 0x0100 or byte.toInt(), vm)
return if(success)
returnValue(callspec.returns.single(), 0x0100 or byte.toInt(), vm)
else
return returnValue(callspec.returns.single(), 0x0000, vm)
returnValue(callspec.returns.single(), 0x0000, vm)
}
Syscall.WRITE_FILE_BYTE -> {
val byte = getArgValues(callspec.arguments, vm).single() as UByte
if(vm.write_file_byte(byte))
return returnValue(callspec.returns.single(), 1, vm)
return if(vm.write_file_byte(byte))
returnValue(callspec.returns.single(), 1, vm)
else
return returnValue(callspec.returns.single(), 0, vm)
returnValue(callspec.returns.single(), 0, vm)
}
Syscall.CLOSE_FILE -> vm.close_file_read()
Syscall.CLOSE_FILE_WRITE -> vm.close_file_write()