From 2b7b9250906ec512801a791f61690c9feda1c233 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 9 Apr 2021 23:08:19 +0200 Subject: [PATCH] codegen now uses correct machine target's string encoder/decoder. Encoding more robust by checking upper case mapping if lowercase mapping fails. --- .../compiler/target/ICompilationTarget.kt | 26 +++++++-- .../src/prog8/compiler/target/cbm/Petscii.kt | 58 ++++++++++++++----- .../compiler/target/cpu6502/codegen/AsmGen.kt | 15 +---- compiler/test/UnitTests.kt | 15 ++--- examples/tehtriz.p8 | 4 +- examples/test.p8 | 12 ++-- 6 files changed, 81 insertions(+), 49 deletions(-) diff --git a/compiler/src/prog8/compiler/target/ICompilationTarget.kt b/compiler/src/prog8/compiler/target/ICompilationTarget.kt index 5c2108645..f6563f3ff 100644 --- a/compiler/src/prog8/compiler/target/ICompilationTarget.kt +++ b/compiler/src/prog8/compiler/target/ICompilationTarget.kt @@ -7,6 +7,7 @@ import prog8.ast.base.* import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.NumericLiteralValue import prog8.ast.statements.AssignTarget +import prog8.compiler.AssemblyError import prog8.compiler.CompilationOptions import prog8.compiler.IErrorReporter import prog8.compiler.Zeropage @@ -14,6 +15,7 @@ import prog8.compiler.target.c64.C64MachineDefinition import prog8.compiler.target.cbm.Petscii import prog8.compiler.target.cpu6502.codegen.AsmGen import prog8.compiler.target.cx16.CX16MachineDefinition +import java.io.CharConversionException import java.nio.file.Path @@ -70,9 +72,17 @@ internal object C64Target: ICompilationTarget { override val name = "c64" override val machine = C64MachineDefinition override fun encodeString(str: String, altEncoding: Boolean) = - if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true) + try { + if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true) + } catch (x: CharConversionException) { + throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}") + } override fun decodeString(bytes: List, altEncoding: Boolean) = - if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true) + try { + if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true) + } catch (x: CharConversionException) { + throw AssemblyError("There was a problem decoding to a string: ${x.message}") + } override fun memorySize(dt: DataType): Int { return when(dt) { @@ -89,9 +99,17 @@ internal object Cx16Target: ICompilationTarget { override val name = "cx16" override val machine = CX16MachineDefinition override fun encodeString(str: String, altEncoding: Boolean) = - if(altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true) + try { + if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true) + } catch (x: CharConversionException) { + throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}") + } override fun decodeString(bytes: List, altEncoding: Boolean) = - if(altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true) + try { + if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true) + } catch (x: CharConversionException) { + throw AssemblyError("There was a problem decoding to a string: ${x.message}") + } override fun memorySize(dt: DataType): Int { return when(dt) { diff --git a/compiler/src/prog8/compiler/target/cbm/Petscii.kt b/compiler/src/prog8/compiler/target/cbm/Petscii.kt index ff4ef9af6..e07c5722a 100644 --- a/compiler/src/prog8/compiler/target/cbm/Petscii.kt +++ b/compiler/src/prog8/compiler/target/cbm/Petscii.kt @@ -1051,49 +1051,75 @@ object Petscii { fun encodePetscii(text: String, lowercase: Boolean = false): List { - val lookup = if(lowercase) encodingPetsciiLowercase else encodingPetsciiUppercase - return text.map { - val petscii = lookup[it] - petscii?.toShort() ?: when (it) { + fun encodeChar(chr: Char, lowercase: Boolean): Short { + val screencode = if(lowercase) encodingPetsciiLowercase[chr] else encodingPetsciiUppercase[chr] + return screencode?.toShort() ?: when (chr) { '\u0000' -> 0.toShort() in '\u8000'..'\u80ff' -> { // special case: take the lower 8 bit hex value directly - (it.toInt() - 0x8000).toShort() + (chr.toInt() - 0x8000).toShort() } else -> { val case = if (lowercase) "lower" else "upper" - throw CharConversionException("no ${case}case Petscii character for '$it' (${it.toShort()})") + throw CharConversionException("no ${case}Petscii character for '$chr' (${chr.toShort()})") } } } + + return text.map{ + try { + encodeChar(it, lowercase) + } catch (x: CharConversionException) { + encodeChar(it, !lowercase) + } + } } fun decodePetscii(petscii: Iterable, lowercase: Boolean = false): String { - val decodeTable = if(lowercase) decodingPetsciiLowercase else decodingPetsciiUppercase - return petscii.map { decodeTable[it.toInt()] }.joinToString("") + return petscii.map { + val code = it.toInt() + try { + if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code] + } catch(x: CharConversionException) { + if(lowercase) decodingPetsciiUppercase[code] else decodingPetsciiLowercase[code] + } + }.joinToString("") } fun encodeScreencode(text: String, lowercase: Boolean = false): List { - val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase - return text.map{ - val screencode = lookup[it] - screencode?.toShort() ?: when (it) { + fun encodeChar(chr: Char, lowercase: Boolean): Short { + val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr] + return screencode?.toShort() ?: when (chr) { '\u0000' -> 0.toShort() in '\u8000'..'\u80ff' -> { // special case: take the lower 8 bit hex value directly - (it.toInt() - 0x8000).toShort() + (chr.toInt() - 0x8000).toShort() } else -> { val case = if (lowercase) "lower" else "upper" - throw CharConversionException("no ${case}Screencode character for '$it' (${it.toShort()})") + throw CharConversionException("no ${case}Screencode character for '$chr' (${chr.toShort()})") } } } + + return text.map{ + try { + encodeChar(it, lowercase) + } catch (x: CharConversionException) { + encodeChar(it, !lowercase) + } + } } fun decodeScreencode(screencode: Iterable, lowercase: Boolean = false): String { - val decodeTable = if(lowercase) decodingScreencodeLowercase else decodingScreencodeUppercase - return screencode.map { decodeTable[it.toInt()] }.joinToString("") + return screencode.map { + val code = it.toInt() + try { + if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code] + } catch (x: CharConversionException) { + if (lowercase) decodingScreencodeUppercase[code] else decodingScreencodeLowercase[code] + } + }.joinToString("") } fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Short { diff --git a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt index 0c5d16c33..64994b5c7 100644 --- a/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/cpu6502/codegen/AsmGen.kt @@ -10,11 +10,9 @@ import prog8.compiler.functions.BuiltinFunctions import prog8.compiler.functions.FSignature import prog8.compiler.target.* import prog8.compiler.target.cbm.AssemblyProgram -import prog8.compiler.target.cbm.Petscii import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen import prog8.optimizer.CallGraph -import java.io.CharConversionException import java.nio.file.Path import java.nio.file.Paths import java.time.LocalDate @@ -257,15 +255,6 @@ internal class AsmGen(private val program: Program, } else assemblyLines.add(fragment) } - private fun encode(str: String, altEncoding: Boolean): List { - try { - val bytes = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true) - return bytes.plus(0) - } catch(x: CharConversionException) { - throw AssemblyError("There was a problem converting a string to the target machine's char encoding: ${x.message}") - } - } - private fun zeropagevars2asm(statements: List) { out("; vars allocated on zeropage") val variables = statements.filterIsInstance().filter { it.type==VarDeclType.VAR } @@ -304,7 +293,7 @@ internal class AsmGen(private val program: Program, DataType.STRUCT -> {} // is flattened DataType.STR -> { val str = decl.value as StringLiteralValue - outputStringvar(decl, encode(str.value, str.altEncoding)) + outputStringvar(decl, compTarget.encodeString(str.value, str.altEncoding).plus(0)) } DataType.ARRAY_UB -> { val data = makeArrayFillDataUnsigned(decl) @@ -401,7 +390,7 @@ internal class AsmGen(private val program: Program, .filter {it.datatype == DataType.STR } .map { val str = it.value as StringLiteralValue - it to encode(str.value, str.altEncoding) + it to compTarget.encodeString(str.value, str.altEncoding).plus(0) } .groupBy({it.second}, {it.first}) for((encoded, variables) in encodedstringVars) { diff --git a/compiler/test/UnitTests.kt b/compiler/test/UnitTests.kt index 8a8521e7d..d968cb1c6 100644 --- a/compiler/test/UnitTests.kt +++ b/compiler/test/UnitTests.kt @@ -348,8 +348,8 @@ class TestPetscii { listOf(72, 69, 76, 76, 79, 32, 0xd7, 0xcf, 0xd2, 0xcc, 0xc4, 32, 49, 50, 51, 32, 64, 33, 0x5c))) assertThat(Petscii.encodePetscii("\uf11a", true), equalTo(listOf(0x12))) // reverse vid assertThat(Petscii.encodePetscii("✓", true), equalTo(listOf(0xfa))) - assertFailsWith { Petscii.encodePetscii("π", true) } - assertFailsWith { Petscii.encodePetscii("♥", true) } + assertThat("expect lowercase error fallback", Petscii.encodePetscii("π", true), equalTo(listOf(255))) + assertThat("expect lowercase error fallback", Petscii.encodePetscii("♥", true), equalTo(listOf(0xd3))) assertThat(Petscii.decodePetscii(listOf(72, 0xd7, 0x5c, 0xfa, 0x12), true), equalTo("hW£✓\uF11A")) assertFailsWith { Petscii.decodePetscii(listOf(-1), true) } @@ -363,7 +363,7 @@ class TestPetscii { assertThat(Petscii.encodePetscii("\uf11a"), equalTo(listOf(0x12))) // reverse vid assertThat(Petscii.encodePetscii("♥"), equalTo(listOf(0xd3))) assertThat(Petscii.encodePetscii("π"), equalTo(listOf(0xff))) - assertFailsWith { Petscii.encodePetscii("✓") } + assertThat("expecting fallback", Petscii.encodePetscii("✓"), equalTo(listOf(250))) assertThat(Petscii.decodePetscii(listOf(72, 0x5c, 0xd3, 0xff)), equalTo("H£♥π")) assertFailsWith { Petscii.decodePetscii(listOf(-1)) } @@ -376,8 +376,8 @@ class TestPetscii { listOf(0x08, 0x05, 0x0c, 0x0c, 0x0f, 0x20, 0x57, 0x4f, 0x52, 0x4c, 0x44, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c) )) assertThat(Petscii.encodeScreencode("✓", true), equalTo(listOf(0x7a))) - assertFailsWith { Petscii.encodeScreencode("♥", true) } - assertFailsWith { Petscii.encodeScreencode("π", true) } + assertThat("expect fallback", Petscii.encodeScreencode("♥", true), equalTo(listOf(83))) + assertThat("expect fallback", Petscii.encodeScreencode("π", true), equalTo(listOf(94))) assertThat(Petscii.decodeScreencode(listOf(0x08, 0x57, 0x1c, 0x7a), true), equalTo("hW£✓")) assertFailsWith { Petscii.decodeScreencode(listOf(-1), true) } @@ -390,8 +390,9 @@ class TestPetscii { listOf(0x17, 0x0f, 0x12, 0x0c, 0x04, 0x20, 0x31, 0x32, 0x33, 0x20, 0x00, 0x21, 0x1c))) assertThat(Petscii.encodeScreencode("♥"), equalTo(listOf(0x53))) assertThat(Petscii.encodeScreencode("π"), equalTo(listOf(0x5e))) - assertFailsWith { Petscii.encodeScreencode("✓") } - assertFailsWith { Petscii.encodeScreencode("hello") } + assertThat(Petscii.encodeScreencode("HELLO"), equalTo(listOf(8, 5, 12, 12, 15))) + assertThat("expecting fallback", Petscii.encodeScreencode("hello"), equalTo(listOf(8, 5, 12, 12, 15))) + assertThat("expecting fallback", Petscii.encodeScreencode("✓"), equalTo(listOf(122))) assertThat(Petscii.decodeScreencode(listOf(0x17, 0x1c, 0x53, 0x5e)), equalTo("W£♥π")) assertFailsWith { Petscii.decodeScreencode(listOf(-1)) } diff --git a/examples/tehtriz.p8 b/examples/tehtriz.p8 index d335d53ee..3fdffd868 100644 --- a/examples/tehtriz.p8 +++ b/examples/tehtriz.p8 @@ -206,7 +206,7 @@ waitkey: num_lines++ ubyte x for x in boardOffsetX to boardOffsetX+boardWidth-1 - txt.setcc(x, linepos, 160, 1) + txt.setcc(x, linepos, @'▒', 1) } } if num_lines { @@ -338,7 +338,7 @@ waitkey: for i in len(colors)-1 downto 0 { ubyte x for x in 5 downto 0 { - txt.setcc(6+x-i, 11+2*i, 102, colors[i]) + txt.setcc(6+x-i, 11+2*i, @'▒', colors[i]) } } drawScore() diff --git a/examples/test.p8 b/examples/test.p8 index 5db98ed9a..4cbbd11cd 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -3,13 +3,11 @@ main { sub start() { - ubyte lives=2 - ubyte lvs - - for lvs in 10 to lives { - txt.print_ub(lvs) - txt.spc() - } + txt.print("✓✓✓✓✓") + txt.nl() + txt.print("WWWWW") + txt.nl() + txt.print("●●●●●") txt.nl() } }