mirror of
https://github.com/irmen/prog8.git
synced 2024-11-16 22:09:56 +00:00
strings of 1 and 2 length no longer optimized into one call to CHROUT - also upgrade to kotlin 1.5.0
This commit is contained in:
parent
09a1de69e7
commit
8736da1a21
@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.0"
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
|
@ -196,51 +196,52 @@ private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICo
|
||||
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||
val mainModule = program.mainModule
|
||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
||||
as? Directive)?.args?.single()?.name?.uppercase()
|
||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }
|
||||
.flatMap { (it as Directive).args }.toSet()
|
||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||
val noSysInit = allOptions.any { it.name == "no_sysinit" }
|
||||
var zpType: ZeropageType =
|
||||
if (zpoption == null)
|
||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
else
|
||||
try {
|
||||
ZeropageType.valueOf(zpoption)
|
||||
} catch (x: IllegalArgumentException) {
|
||||
ZeropageType.KERNALSAFE
|
||||
// error will be printed by the astchecker
|
||||
}
|
||||
if (zpoption == null)
|
||||
if (floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
else
|
||||
try {
|
||||
ZeropageType.valueOf(zpoption)
|
||||
} catch (x: IllegalArgumentException) {
|
||||
ZeropageType.KERNALSAFE
|
||||
// error will be printed by the astchecker
|
||||
}
|
||||
|
||||
if (zpType==ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
|
||||
if (zpType == ZeropageType.FLOATSAFE && compTarget.name == Cx16Target.name) {
|
||||
System.err.println("Warning: zp option floatsafe changed to basicsafe for cx16 target")
|
||||
zpType = ZeropageType.BASICSAFE
|
||||
}
|
||||
|
||||
val zpReserved = mainModule.statements
|
||||
.asSequence()
|
||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||
.map { (it as Directive).args }
|
||||
.map { it[0].int!!..it[1].int!! }
|
||||
.toList()
|
||||
.asSequence()
|
||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||
.map { (it as Directive).args }
|
||||
.map { it[0].int!!..it[1].int!! }
|
||||
.toList()
|
||||
|
||||
if(outputType!=null && !OutputType.values().any {it.name==outputType}) {
|
||||
if (outputType != null && !OutputType.values().any { it.name == outputType }) {
|
||||
System.err.println("invalid output type $outputType")
|
||||
exitProcess(1)
|
||||
}
|
||||
if(launcherType!=null && !LauncherType.values().any {it.name==launcherType}) {
|
||||
if (launcherType != null && !LauncherType.values().any { it.name == launcherType }) {
|
||||
System.err.println("invalid launcher type $launcherType")
|
||||
exitProcess(1)
|
||||
}
|
||||
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||
compTarget
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled, noSysInit,
|
||||
compTarget
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,7 @@ import prog8.compiler.target.C64Target
|
||||
import prog8.compiler.target.Cx16Target
|
||||
import prog8.compiler.target.ICompilationTarget
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
internal class AstChecker(private val program: Program,
|
||||
private val compilerOptions: CompilationOptions,
|
||||
@ -1029,7 +1030,7 @@ internal class AstChecker(private val program: Program,
|
||||
ident = fcall.args[0] as? IdentifierReference
|
||||
}
|
||||
if(ident!=null && ident.nameInSource[0] == "cx16" && ident.nameInSource[1].startsWith("r")) {
|
||||
val reg = RegisterOrPair.valueOf(ident.nameInSource[1].toUpperCase())
|
||||
val reg = RegisterOrPair.valueOf(ident.nameInSource[1].uppercase())
|
||||
val same = params.filter { it.value.registerOrPair==reg }
|
||||
for(s in same) {
|
||||
if(s.index!=arg.index) {
|
||||
@ -1355,10 +1356,15 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("cannot assign word to byte, use msb() or lsb()?", position)
|
||||
}
|
||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
errors.err("cannot assign float to ${targetDatatype.name.toLowerCase()}; 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 {
|
||||
if(targetDatatype!=DataType.UWORD && sourceDatatype !in PassByReferenceDatatypes)
|
||||
errors.err("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position)
|
||||
errors.err(
|
||||
"cannot assign ${sourceDatatype.name.lowercase()} to ${
|
||||
targetDatatype.name.lowercase(
|
||||
Locale.getDefault()
|
||||
)
|
||||
}", position)
|
||||
}
|
||||
|
||||
|
||||
|
@ -153,7 +153,7 @@ fun builtinMax(array: List<Number>): Number = array.maxByOrNull { it.toDouble()
|
||||
|
||||
fun builtinMin(array: List<Number>): Number = array.minByOrNull { it.toDouble() }!!
|
||||
|
||||
fun builtinSum(array: List<Number>): Number = array.sumByDouble { it.toDouble() }
|
||||
fun builtinSum(array: List<Number>): Number = array.sumOf { it.toDouble() }
|
||||
|
||||
fun builtinAny(array: List<Number>): Number = if(array.any { it.toDouble()!=0.0 }) 1 else 0
|
||||
|
||||
|
@ -1069,11 +1069,11 @@ object Petscii {
|
||||
'\u0000' -> 0.toShort()
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.toInt() - 0x8000).toShort()
|
||||
(chr.code - 0x8000).toShort()
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Petscii character for '$chr' (${chr.toShort()})")
|
||||
throw CharConversionException("no ${case}Petscii character for '$chr' (${chr.code})")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1106,11 +1106,11 @@ object Petscii {
|
||||
'\u0000' -> 0.toShort()
|
||||
in '\u8000'..'\u80ff' -> {
|
||||
// special case: take the lower 8 bit hex value directly
|
||||
(chr.toInt() - 0x8000).toShort()
|
||||
(chr.code - 0x8000).toShort()
|
||||
}
|
||||
else -> {
|
||||
val case = if (lowercase) "lower" else "upper"
|
||||
throw CharConversionException("no ${case}Screencode character for '$chr' (${chr.toShort()})")
|
||||
throw CharConversionException("no ${case}Screencode character for '$chr' (${chr.code})")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -351,6 +351,9 @@ internal class AsmGen(private val program: Program,
|
||||
for (f in array.zip(floatFills))
|
||||
out(" .byte ${f.second} ; float ${f.first}")
|
||||
}
|
||||
else -> {
|
||||
throw AssemblyError("weird dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -498,8 +501,8 @@ internal class AsmGen(private val program: Program,
|
||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||
|
||||
internal fun asmSymbolName(regs: RegisterOrPair): String =
|
||||
if(regs in Cx16VirtualRegisters)
|
||||
"cx16." + regs.toString().toLowerCase()
|
||||
if (regs in Cx16VirtualRegisters)
|
||||
"cx16." + regs.toString().lowercase()
|
||||
else
|
||||
throw AssemblyError("no symbol name for register $regs")
|
||||
|
||||
@ -689,14 +692,16 @@ internal class AsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadScaledArrayIndexIntoRegister(expr: ArrayIndexedExpression,
|
||||
elementDt: DataType,
|
||||
register: CpuRegister,
|
||||
addOneExtra: Boolean=false) {
|
||||
val reg = register.toString().toLowerCase()
|
||||
internal fun loadScaledArrayIndexIntoRegister(
|
||||
expr: ArrayIndexedExpression,
|
||||
elementDt: DataType,
|
||||
register: CpuRegister,
|
||||
addOneExtra: Boolean = false
|
||||
) {
|
||||
val reg = register.toString().lowercase()
|
||||
val indexnum = expr.indexer.constIndex()
|
||||
if(indexnum!=null) {
|
||||
val indexValue = indexnum * compTarget.memorySize(elementDt) + if(addOneExtra) 1 else 0
|
||||
if (indexnum != null) {
|
||||
val indexValue = indexnum * compTarget.memorySize(elementDt) + if (addOneExtra) 1 else 0
|
||||
out(" ld$reg #$indexValue")
|
||||
return
|
||||
}
|
||||
@ -705,35 +710,40 @@ internal class AsmGen(private val program: Program,
|
||||
?: throw AssemblyError("array indexer should have been replaced with a temp var @ ${expr.indexer.position}")
|
||||
|
||||
val indexName = asmVariableName(indexVar)
|
||||
if(addOneExtra) {
|
||||
if (addOneExtra) {
|
||||
// add 1 to the result
|
||||
when(elementDt) {
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> {
|
||||
out(" ldy $indexName | iny")
|
||||
when(register) {
|
||||
when (register) {
|
||||
CpuRegister.A -> out(" tya")
|
||||
CpuRegister.X -> out(" tyx")
|
||||
CpuRegister.Y -> {}
|
||||
CpuRegister.Y -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
out(" lda $indexName | sec | rol a")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(compTarget.memorySize(DataType.FLOAT)==5)
|
||||
out("""
|
||||
require(compTarget.memorySize(DataType.FLOAT) == 5)
|
||||
out(
|
||||
"""
|
||||
lda $indexName
|
||||
asl a
|
||||
asl a
|
||||
sec
|
||||
adc $indexName""")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
adc $indexName"""
|
||||
)
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
@ -741,26 +751,30 @@ internal class AsmGen(private val program: Program,
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
} else {
|
||||
when(elementDt) {
|
||||
when (elementDt) {
|
||||
in ByteDatatypes -> out(" ld$reg $indexName")
|
||||
in WordDatatypes -> {
|
||||
out(" lda $indexName | asl a")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
require(compTarget.memorySize(DataType.FLOAT)==5)
|
||||
out("""
|
||||
require(compTarget.memorySize(DataType.FLOAT) == 5)
|
||||
out(
|
||||
"""
|
||||
lda $indexName
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $indexName""")
|
||||
when(register) {
|
||||
CpuRegister.A -> {}
|
||||
adc $indexName"""
|
||||
)
|
||||
when (register) {
|
||||
CpuRegister.A -> {
|
||||
}
|
||||
CpuRegister.X -> out(" tax")
|
||||
CpuRegister.Y -> out(" tay")
|
||||
}
|
||||
|
@ -1260,7 +1260,10 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
RegisterOrPair.AY -> {}
|
||||
RegisterOrPair.AX -> asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG")
|
||||
RegisterOrPair.XY -> asmgen.out(" tax")
|
||||
in Cx16VirtualRegisters -> asmgen.out(" sta cx16.${resultRegister.toString().toLowerCase()} | sty cx16.${resultRegister.toString().toLowerCase()}+1")
|
||||
in Cx16VirtualRegisters -> asmgen.out(
|
||||
" sta cx16.${
|
||||
resultRegister.toString().lowercase()
|
||||
} | sty cx16.${resultRegister.toString().lowercase()}+1")
|
||||
else -> throw AssemblyError("invalid reg")
|
||||
}
|
||||
}
|
||||
@ -1310,9 +1313,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
||||
}
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.assignExpressionToRegister(fcall.args[1], RegisterOrPair.A) // lsb
|
||||
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}")
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}")
|
||||
asmgen.assignExpressionToRegister(fcall.args[0], RegisterOrPair.A) // msb
|
||||
asmgen.out(" sta cx16.${reg.toString().toLowerCase()}+1")
|
||||
asmgen.out(" sta cx16.${reg.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid mkword target reg")
|
||||
}
|
||||
|
@ -1573,10 +1573,11 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
||||
RegisterOrPair.R13,
|
||||
RegisterOrPair.R14,
|
||||
RegisterOrPair.R15 -> {
|
||||
asmgen.out("""
|
||||
lda cx16.${reg.registerOrPair.toString().toLowerCase()}
|
||||
asmgen.out(
|
||||
"""
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}
|
||||
sta P8ESTACK_LO,x
|
||||
lda cx16.${reg.registerOrPair.toString().toLowerCase()}+1
|
||||
lda cx16.${reg.registerOrPair.toString().lowercase()}+1
|
||||
sta P8ESTACK_HI,x
|
||||
dex
|
||||
""")
|
||||
|
@ -175,21 +175,31 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
when (sub.parameters[argi.index].type) {
|
||||
in ByteDatatypes -> {
|
||||
// only load the lsb of the virtual register
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||
""")
|
||||
if (asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" lda #0 | sta cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1")
|
||||
}
|
||||
in WordDatatypes, in IterableDatatypes ->
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda P8ESTACK_LO$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().lowercase()}
|
||||
lda P8ESTACK_HI$plusIdxStr,x
|
||||
sta cx16.${argi.value.second.registerOrPair.toString().toLowerCase()}+1
|
||||
sta cx16.${
|
||||
argi.value.second.registerOrPair.toString().lowercase()
|
||||
}+1
|
||||
""")
|
||||
else -> throw AssemblyError("weird dt")
|
||||
}
|
||||
|
@ -135,9 +135,9 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val dt = value.inferType(program).typeOrElse(DataType.UNDEFINED)
|
||||
val varName=asmgen.asmVariableName(value)
|
||||
// special case: "cx16.r[0-15]" are 16-bits virtual registers of the commander X16 system
|
||||
if(dt==DataType.UWORD && varName.toLowerCase().startsWith("cx16.r")) {
|
||||
val regStr = varName.toLowerCase().substring(5)
|
||||
val reg = RegisterOrPair.valueOf(regStr.toUpperCase())
|
||||
if(dt == DataType.UWORD && varName.lowercase().startsWith("cx16.r")) {
|
||||
val regStr = varName.lowercase().substring(5)
|
||||
val reg = RegisterOrPair.valueOf(regStr.uppercase())
|
||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, dt, register = reg)
|
||||
} else {
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, dt, variableAsmName = varName)
|
||||
|
@ -587,13 +587,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.UBYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(regs) {
|
||||
@ -615,13 +621,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.BYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | stz $targetAsmVarName+1")
|
||||
else
|
||||
asmgen.out(" st${regs.toString().toLowerCase()} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
asmgen.out(
|
||||
" st${
|
||||
regs.toString().lowercase()
|
||||
} $targetAsmVarName | lda #0 | sta $targetAsmVarName+1")
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when(regs) {
|
||||
@ -653,7 +665,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.UWORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
when(regs) {
|
||||
@ -681,7 +693,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
DataType.WORD -> {
|
||||
when(targetDt) {
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().toLowerCase().first()} $targetAsmVarName")
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
when(regs) {
|
||||
@ -822,12 +834,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> asmgen.out(" inx | txy | ldx #0 | lda P8ESTACK_LO,y")
|
||||
RegisterOrPair.AY -> asmgen.out(" inx | ldy #0 | lda P8ESTACK_LO,x")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't assign byte from stack to register pair XY")
|
||||
@ -839,12 +852,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY-> asmgen.out(" inx | ldy P8ESTACK_HI,x | lda P8ESTACK_LO,x")
|
||||
RegisterOrPair.XY-> throw AssemblyError("can't load X from stack here - use intermediary var? ${target.origAstTarget?.position}")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
inx
|
||||
lda P8ESTACK_LO,x
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda P8ESTACK_HI,x
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't assign word to single byte register")
|
||||
@ -888,11 +902,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy #>$sourceName | lda #<$sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #>$sourceName | ldx #<$sourceName")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda #<$sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #>$sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't load address in a single 8-bit register")
|
||||
@ -1029,11 +1044,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy $sourceName+1 | lda $sourceName")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy $sourceName+1 | ldx $sourceName")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda $sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda $sourceName+1
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("can't load word in a single 8-bit register")
|
||||
@ -1203,11 +1219,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx $sourceName")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda $sourceName
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
@ -1355,7 +1372,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
|
||||
when(target.kind) {
|
||||
TargetStorageKind.VARIABLE -> {
|
||||
asmgen.out(" st${register.name.toLowerCase()} ${target.asmVarname}")
|
||||
asmgen.out(" st${register.name.lowercase()} ${target.asmVarname}")
|
||||
}
|
||||
TargetStorageKind.MEMORY -> {
|
||||
when(register) {
|
||||
@ -1395,7 +1412,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
asmgen.out(" sta cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(" sta cx16.${target.register.toString().lowercase()}")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@ -1409,7 +1426,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
asmgen.out(" stx cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(" stx cx16.${target.register.toString().lowercase()}")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@ -1423,7 +1440,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected type cast to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
// only assign a single byte to the virtual register's Lsb
|
||||
asmgen.out(" sty cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(" sty cx16.${target.register.toString().lowercase()}")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@ -1513,9 +1530,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> { }
|
||||
RegisterOrPair.XY -> { asmgen.out(" stx P8ZP_SCRATCH_REG | ldy P8ZP_SCRATCH_REG | tax") }
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
stx cx16.${target.register.toString().toLowerCase()}+1
|
||||
asmgen.out(
|
||||
"""
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
stx cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||
@ -1525,9 +1543,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> { asmgen.out(" sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||
RegisterOrPair.XY -> { asmgen.out(" tax") }
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sty cx16.${target.register.toString().toLowerCase()}+1
|
||||
asmgen.out(
|
||||
"""
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
sty cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||
@ -1537,9 +1556,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AX -> { asmgen.out(" txa | sty P8ZP_SCRATCH_REG | ldx P8ZP_SCRATCH_REG") }
|
||||
RegisterOrPair.XY -> { }
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
stx cx16.${target.register.toString().toLowerCase()}
|
||||
sty cx16.${target.register.toString().toLowerCase()}+1
|
||||
asmgen.out(
|
||||
"""
|
||||
stx cx16.${target.register.toString().lowercase()}
|
||||
sty cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("expected reg pair or cx16 virtual 16-bit register")
|
||||
@ -1606,7 +1626,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" lda #0 | tay")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()} | stz cx16.${target.register.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
target.register.toString().lowercase()
|
||||
} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid register for word value")
|
||||
}
|
||||
@ -1656,11 +1679,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.AY -> asmgen.out(" ldy #>${word.toHex()} | lda #<${word.toHex()}")
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #>${word.toHex()} | ldx #<${word.toHex()}")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda #<${word.toHex()}
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #>${word.toHex()}
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid register for word value")
|
||||
@ -1707,7 +1731,10 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldx #0 | ldy #0")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()} | stz cx16.${target.register.toString().toLowerCase()}+1")
|
||||
asmgen.out(
|
||||
" stz cx16.${
|
||||
target.register.toString().lowercase()
|
||||
} | stz cx16.${target.register.toString().lowercase()}+1")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@ -1747,11 +1774,17 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldx #${byte.toHex()}")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out(" lda #${byte.toHex()} | sta cx16.${target.register.toString().toLowerCase()}")
|
||||
asmgen.out(
|
||||
" lda #${byte.toHex()} | sta cx16.${
|
||||
target.register.toString().lowercase()
|
||||
}")
|
||||
if(asmgen.isTargetCpu(CpuType.CPU65c02))
|
||||
asmgen.out(" stz cx16.${target.register.toString().toLowerCase()}+1\n")
|
||||
asmgen.out(" stz cx16.${target.register.toString().lowercase()}+1\n")
|
||||
else
|
||||
asmgen.out(" lda #0 | sta cx16.${target.register.toString().toLowerCase()}+1\n")
|
||||
asmgen.out(
|
||||
" lda #0 | sta cx16.${
|
||||
target.register.toString().lowercase()
|
||||
}+1\n")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
}
|
||||
@ -1927,11 +1960,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" ldy #0 | ldy ${address.toHex()}")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
asmgen.out(
|
||||
"""
|
||||
lda ${address.toHex()}
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
@ -1967,10 +2001,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
||||
RegisterOrPair.XY -> asmgen.out(" tax | ldy #0")
|
||||
RegisterOrPair.FAC1, RegisterOrPair.FAC2 -> throw AssemblyError("expected typecasted byte to float")
|
||||
in Cx16VirtualRegisters -> {
|
||||
asmgen.out("""
|
||||
sta cx16.${target.register.toString().toLowerCase()}
|
||||
asmgen.out(
|
||||
"""
|
||||
sta cx16.${target.register.toString().lowercase()}
|
||||
lda #0
|
||||
sta cx16.${target.register.toString().toLowerCase()}+1
|
||||
sta cx16.${target.register.toString().lowercase()}+1
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("weird register")
|
||||
|
@ -3,7 +3,6 @@ package prog8.optimizer
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.AddressOf
|
||||
|
@ -43,48 +43,6 @@ internal class StatementOptimizer(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
|
||||
if(functionCallStatement.target.nameInSource==listOf("txt", "print")) {
|
||||
val arg = functionCallStatement.args.single()
|
||||
val stringVar: IdentifierReference? = if(arg is AddressOf) {
|
||||
arg.identifier
|
||||
} else {
|
||||
arg as? IdentifierReference
|
||||
}
|
||||
if(stringVar!=null) {
|
||||
val vardecl = stringVar.targetVarDecl(program)!!
|
||||
val string = vardecl.value as? StringLiteralValue
|
||||
if(string!=null) {
|
||||
val pos = functionCallStatement.position
|
||||
if (string.value.length == 1) {
|
||||
val firstCharEncoded = compTarget.encodeString(string.value, string.altEncoding)[0]
|
||||
val chrout = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstCharEncoded.toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(IAstModification.ReplaceNode(functionCallStatement, chrout, parent))
|
||||
} else if (string.value.length == 2) {
|
||||
val firstTwoCharsEncoded = compTarget.encodeString(string.value.take(2), string.altEncoding)
|
||||
val chrout1 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[0].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
val chrout2 = FunctionCallStatement(
|
||||
IdentifierReference(listOf("txt", "chrout"), pos),
|
||||
mutableListOf(NumericLiteralValue(DataType.UBYTE, firstTwoCharsEncoded[1].toInt(), pos)),
|
||||
functionCallStatement.void, pos
|
||||
)
|
||||
return listOf(
|
||||
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as INameScope),
|
||||
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the first instruction in the called subroutine is a return statement, remove the jump altogeter
|
||||
val subroutine = functionCallStatement.target.targetSubroutine(program)
|
||||
if(subroutine!=null) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
plugins {
|
||||
id 'antlr'
|
||||
id 'java'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.0"
|
||||
}
|
||||
|
||||
targetCompatibility = 11
|
||||
|
@ -7,6 +7,7 @@ import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import java.util.*
|
||||
|
||||
|
||||
class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
|
||||
@ -78,9 +79,9 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
}
|
||||
|
||||
private fun datatypeString(dt: DataType): String {
|
||||
return when(dt) {
|
||||
in NumericDatatypes -> dt.toString().toLowerCase()
|
||||
DataType.STR -> dt.toString().toLowerCase()
|
||||
return when (dt) {
|
||||
in NumericDatatypes -> dt.toString().lowercase()
|
||||
DataType.STR -> dt.toString().lowercase()
|
||||
DataType.ARRAY_UB -> "ubyte["
|
||||
DataType.ARRAY_B -> "byte["
|
||||
DataType.ARRAY_UW -> "uword["
|
||||
@ -235,7 +236,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
}
|
||||
|
||||
override fun visit(branchStatement: BranchStatement) {
|
||||
output("if_${branchStatement.condition.toString().toLowerCase()} ")
|
||||
output("if_${branchStatement.condition.toString().lowercase()} ")
|
||||
branchStatement.truepart.accept(this)
|
||||
if(branchStatement.elsepart.statements.isNotEmpty()) {
|
||||
output(" else ")
|
||||
|
@ -13,6 +13,7 @@ import prog8.parser.prog8Parser
|
||||
import java.io.CharConversionException
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
|
||||
/***************** Antlr Extension methods to create AST ****************/
|
||||
@ -340,7 +341,7 @@ private fun prog8Parser.ClobberContext.toAst() : Set<CpuRegister> {
|
||||
return names.map { CpuRegister.valueOf(it) }.toSet()
|
||||
}
|
||||
|
||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.uppercase())
|
||||
|
||||
private fun prog8Parser.ArrayindexContext.toAst(encoding: IStringEncoding) : ArrayIndex =
|
||||
ArrayIndex(expression().toAst(encoding), toPosition())
|
||||
@ -536,7 +537,9 @@ private fun prog8Parser.Branch_stmtContext.toAst(encoding: IStringEncoding): Bra
|
||||
return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(
|
||||
text.substringAfter('_').uppercase()
|
||||
)
|
||||
|
||||
private fun prog8Parser.ForloopContext.toAst(encoding: IStringEncoding): ForLoop {
|
||||
val loopvar = identifier().toAst()
|
||||
|
@ -2,7 +2,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.0"
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
- mark vardecls 'shared' to indicate they should not be optimized away because they're shared with assembly code??
|
||||
- github issue: make strings no longer immutable? fixes weird optimization issue. Deduplication via command line switch?
|
||||
- test all examples before release of the new version
|
||||
|
||||
- simplify cx16.joystick_get2() once this cx16 rom issue is resolved: https://github.com/commanderx16/x16-rom/issues/203
|
||||
|
@ -1,37 +1,15 @@
|
||||
%import textio
|
||||
%import diskio
|
||||
%zeropage basicsafe
|
||||
%option no_sysinit
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
str message = "a"
|
||||
|
||||
sub start() {
|
||||
|
||||
txt.print(diskio.status(8))
|
||||
txt.nl()
|
||||
txt.print(diskio.status(9))
|
||||
txt.nl()
|
||||
; txt.print(diskio.status(10))
|
||||
; txt.nl()
|
||||
; txt.print(diskio.status(11))
|
||||
; txt.nl()
|
||||
|
||||
uword x=22
|
||||
x*=320
|
||||
txt.print_uw(x)
|
||||
txt.nl()
|
||||
|
||||
x=22
|
||||
x*=640
|
||||
txt.print_uw(x)
|
||||
txt.nl()
|
||||
|
||||
ubyte a=1
|
||||
ubyte b=22
|
||||
|
||||
x = (a*b)*640*a
|
||||
txt.print_uw(x)
|
||||
txt.nl()
|
||||
|
||||
|
||||
}
|
||||
txt.print(message)
|
||||
txt.nl()
|
||||
message[0] = '@'
|
||||
txt.print(message)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'application'
|
||||
id "org.jetbrains.kotlin.jvm" version "1.4.32"
|
||||
id "org.jetbrains.kotlin.jvm" version "1.5.0"
|
||||
id 'com.github.johnrengelman.shadow' version '6.1.0'
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user