diff --git a/.idea/modules.xml b/.idea/modules.xml
index fe1d035d6..374e6bcbb 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -7,6 +7,7 @@
+
\ No newline at end of file
diff --git a/compiler/src/prog8/CompilerMain.kt b/compiler/src/prog8/CompilerMain.kt
index 369e836c7..e5f909ce6 100644
--- a/compiler/src/prog8/CompilerMain.kt
+++ b/compiler/src/prog8/CompilerMain.kt
@@ -8,7 +8,6 @@ import prog8.parser.ParsingFailedError
import prog8.vm.astvm.AstVm
import java.nio.file.FileSystems
import java.nio.file.Path
-import java.nio.file.Paths
import java.nio.file.StandardWatchEventKinds
import java.util.*
import kotlin.system.exitProcess
diff --git a/examples/arithmetic/bcd.asm b/examples/arithmetic/bcd.asm
new file mode 100644
index 000000000..0752404e8
--- /dev/null
+++ b/examples/arithmetic/bcd.asm
@@ -0,0 +1,228 @@
+;===========================================================================
+;
+; Based on: Bruce Clark: a program to verify decimal mode operation
+; www.6502.org/tutorials/decimal_mode.html
+;
+; NMOS 6502\6510: program for testing accumulator result and flag result
+; of ADC and SBC in decimal mode,
+; including correct incorrectness of NVZ flags.
+;
+;...........................................................................
+;
+; ttlworks 04/2016: modified the code for testing the 6510 in a C64.
+;
+;===========================================================================
+
+ERROR = $0400 ; 0='test passed', 1='test failed'
+N1 = $0401 ; first number to be added/subtrected
+N2 = $0402 ; second number to be added/subtrected
+
+N1L = $00F7 ; Bit 3..0 of first number
+N1H = $00F8 ; Bit 7..4 of first number
+N2L = $00F9 ; Bit 3..0 of second number
+N2H = $00FA ; Bit 7..4 of second number
+N2HH = N2H+1 ; Bit 7..4 of second number OR $0F
+
+AR = $00FC ; predicted ACC result in decimal mode
+DA = $00FD ; actual ACC result in decimal mode
+HNVZC = $00FE ; predicted flag result in decimal mode
+DNVZC = $00FF ; actual flag result in decimal mode
+
+;--------------------------------------------------------------------------
+
+ .CPU "6502"
+ * = $1000
+
+TEST LDY #1 ; initialize Y (used to loop through carry flag values)
+ STY ERROR ; store 1 in ERROR until the test passes
+
+ LDA #0 ; initialize N1 and N2
+ STA N1
+ STA N2
+
+LOOP1 LDA N2 ; N2L = N2 & #$0F
+ TAX
+ AND #$0F
+ STA N2L
+
+ TXA ; N2H = N2 & #$F0
+ AND #$F0
+ STA N2H
+ ORA #$0F ; N2HH = (N2 & #$F0) OR #$0F
+ STA N2HH
+
+LOOP2 LDA N1
+ TAX
+ AND #$0F
+ STA N1L
+
+ TXA
+ AND #$F0
+ STA N1H
+
+ JSR ADD
+ BNE DONE
+
+ JSR SUB
+ BNE DONE
+
+ INC N1
+ BNE LOOP2 ; loop through all 256 values of N1
+
+ INC N2
+ BNE LOOP1 ; loop through all 256 values of N2
+
+ DEY
+ BPL LOOP1 ; loop through both values of the C_Flag
+
+ LDA #0 ; test passed, so store 0 in ERROR
+ STA ERROR
+
+DONE RTS
+
+;--------------------------------------------------------------------------
+;
+; N1 + N2
+;
+; calculate the actual decimal mode accumulator and flag results,
+; calculate the actual binary mode accumulator and flag results
+;
+; calculate the predicted decimal mode accumulator and flag results
+; by using binary arithmetic
+;
+ADD ; actual decimal
+
+ SED ; decimal mode
+ CPY #1 ; set carry if Y=1, clear carry if Y=0
+ LDA N1
+ ADC N2
+ STA DA ; actual accumulator result in decimal mode
+ PHP
+ PLA
+ STA DNVZC ; actual flag result in decimal mode
+ CLD ; binary mode
+
+ ; predicted decimal
+
+ CPY #1 ; set carry if Y=1, clear carry if Y=0
+ LDA N1L
+ ADC N2L
+ CMP #$0A
+ AND #$0F
+ PHA
+ LDX #0
+ STX HNVZC
+ BCC A1
+
+ INX
+ ADC #5 ; add 6 (carry is set)
+ AND #$0F
+ SEC
+
+A1 ORA N1H
+;
+; if N1L + N2L < $0A, then add N2 AND $F0
+; if N1L + N2L >= $0A, then add (N2 AND $F0) + $0F +1 (carry is set)
+;
+ ADC N2H,X
+ BCS A2
+ CMP #$A0
+ BCC A3
+
+A2 ADC #$5F ; add #$60 (carry is set)
+ SEC
+
+A3 STA AR ; predicted decimal mode accumulator result
+ ROL HNVZC ; predicted decimal mode C_Flag
+
+ CPX #1
+ PLA ; evaluate NVZ
+ ORA N1H
+ ADC N2H,X
+
+ JMP EV1 ; evaluate/compare
+
+;--------------------------------------------------------------------------
+;
+; N1 - N2
+;
+; calculate the actual decimal mode accumulator and flag results,
+; calculate the actual binary mode accumulator and flag results
+;
+;
+; calculate the predicted decimal mode accumulator and flag results
+; by using binary arithmetic
+
+SUB ; actual decimal
+
+ SED ; decimal mode
+ CPY #1 ; set carry if Y=1, clear carry if Y=0
+ LDA N1
+ SBC N2
+ STA DA ; actual accumulator result in decimal mode
+ PHP
+ PLA
+ STA DNVZC ; actual flag result in decimal mode
+ CLD ; binary mode
+
+ ; predicted decimal
+
+SUB1 CPY #1 ; set carry if Y=1, clear carry if Y=0
+ LDA N1L
+ SBC N2L
+ AND #$0F
+ LDX #0
+ STX HNVZC
+ PHA
+ PHP
+ BCS S11
+
+ INX
+ SBC #5 ; subtract 6 (carry is clear)
+ AND #$0F
+ CLC
+
+S11 ORA N1H
+;
+; if N1L - N2L >= 0, then subtract N2 AND $F0
+; if N1L - N2L < 0, then subtract (N2 AND $F0) + $0F + 1 (carry is clear)
+;
+ SBC N2H,X
+ BCS S12
+ SBC #$5F ; subtract #$60 (carry is clear)
+ CLC
+
+S12 STA AR
+ ROL HNVZC
+
+ PLP ; evaluate NVZ
+ PLA
+ ORA N1H
+ SBC N2H,X
+
+EV1 PHP
+ PLA
+ AND #$C2
+ ORA HNVZC
+ STA HNVZC
+
+;..........................................................................
+;compare actual results to predicted results
+;
+; Return: Z_Flag = 1 (BEQ branch) if same
+; Z_Flag = 0 (BNE branch) if different
+
+COMPARE
+
+ ; LDA HNVZC
+ EOR DNVZC
+ AND #$C3
+ BNE C1
+
+ LDA AR ; accumulator
+ CMP DA
+
+C1 RTS
+
+;--------------------------------------------------------------------------
+
diff --git a/settings.gradle b/settings.gradle
index 9eb378fba..aa92d1e42 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,3 @@
include ':parser'
-include ':compiler'
\ No newline at end of file
+include ':compiler'
+include ':sim65'
diff --git a/sim65/build.gradle b/sim65/build.gradle
new file mode 100644
index 000000000..b29d0ca45
--- /dev/null
+++ b/sim65/build.gradle
@@ -0,0 +1,108 @@
+buildscript {
+ dependencies {
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
+ }
+}
+
+plugins {
+ // id "org.jetbrains.kotlin.jvm" version $kotlinVersion
+ id 'application'
+ id 'org.jetbrains.dokka' version "0.9.18"
+ id 'com.github.johnrengelman.shadow' version '5.1.0'
+ id 'java'
+}
+
+apply plugin: "kotlin"
+apply plugin: "java"
+
+targetCompatibility = 1.8
+sourceCompatibility = 1.8
+
+repositories {
+ mavenLocal()
+ mavenCentral()
+ jcenter()
+ maven { url "https://dl.bintray.com/orangy/maven/" }
+}
+
+def sim65version = rootProject.file('sim65/res/version.txt').text.trim()
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
+ // implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
+ // runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
+
+ testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:$kotlinVersion"
+ testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2'
+ testImplementation 'org.hamcrest:hamcrest-junit:2.0.0.0'
+ testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2'
+ compile 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
+}
+
+compileKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ // verbose = true
+ // freeCompilerArgs += "-XXLanguage:+NewInference"
+ }
+}
+
+compileTestKotlin {
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+}
+
+sourceSets {
+ main {
+ java {
+ srcDirs = ["${project.projectDir}/src"]
+ }
+ resources {
+ srcDirs = ["${project.projectDir}/res"]
+ }
+ }
+ test {
+ java {
+ srcDirs = ["${project.projectDir}/test"]
+ }
+ }
+}
+
+startScripts.enabled = true
+
+application {
+ mainClassName = 'sim65.Sim65MainKt'
+ applicationName = 'sim65'
+}
+
+artifacts {
+ archives shadowJar
+}
+
+
+shadowJar {
+ baseName = 'sim65'
+ version = sim65version
+ // minimize()
+}
+
+
+test {
+ // Enable JUnit 5 (Gradle 4.6+).
+ useJUnitPlatform()
+
+ // Always run tests, even when nothing changed.
+ dependsOn 'cleanTest'
+
+ // Show test results.
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
+
+
+dokka {
+ outputFormat = 'html'
+ outputDirectory = "$buildDir/kdoc"
+}
diff --git a/sim65/res/version.txt b/sim65/res/version.txt
new file mode 100644
index 000000000..49d59571f
--- /dev/null
+++ b/sim65/res/version.txt
@@ -0,0 +1 @@
+0.1
diff --git a/sim65/sim65.iml b/sim65/sim65.iml
new file mode 100644
index 000000000..9c96a55d9
--- /dev/null
+++ b/sim65/sim65.iml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sim65/src/C64Screencodes.kt b/sim65/src/C64Screencodes.kt
new file mode 100644
index 000000000..5497de1bf
--- /dev/null
+++ b/sim65/src/C64Screencodes.kt
@@ -0,0 +1,550 @@
+package sim65
+
+import java.io.CharConversionException
+
+object C64Screencodes {
+
+ // decoding: from Screencodes (0-255) to unicode
+ // character tables used from https://github.com/dj51d/cbmcodecs
+
+ private val decodingScreencodeLowercase = arrayOf(
+ '@' , // @ 0x00 -> COMMERCIAL AT
+ 'a' , // a 0x01 -> LATIN SMALL LETTER A
+ 'b' , // b 0x02 -> LATIN SMALL LETTER B
+ 'c' , // c 0x03 -> LATIN SMALL LETTER C
+ 'd' , // d 0x04 -> LATIN SMALL LETTER D
+ 'e' , // e 0x05 -> LATIN SMALL LETTER E
+ 'f' , // f 0x06 -> LATIN SMALL LETTER F
+ 'g' , // g 0x07 -> LATIN SMALL LETTER G
+ 'h' , // h 0x08 -> LATIN SMALL LETTER H
+ 'i' , // i 0x09 -> LATIN SMALL LETTER I
+ 'j' , // j 0x0A -> LATIN SMALL LETTER J
+ 'k' , // k 0x0B -> LATIN SMALL LETTER K
+ 'l' , // l 0x0C -> LATIN SMALL LETTER L
+ 'm' , // m 0x0D -> LATIN SMALL LETTER M
+ 'n' , // n 0x0E -> LATIN SMALL LETTER N
+ 'o' , // o 0x0F -> LATIN SMALL LETTER O
+ 'p' , // p 0x10 -> LATIN SMALL LETTER P
+ 'q' , // q 0x11 -> LATIN SMALL LETTER Q
+ 'r' , // r 0x12 -> LATIN SMALL LETTER R
+ 's' , // s 0x13 -> LATIN SMALL LETTER S
+ 't' , // t 0x14 -> LATIN SMALL LETTER T
+ 'u' , // u 0x15 -> LATIN SMALL LETTER U
+ 'v' , // v 0x16 -> LATIN SMALL LETTER V
+ 'w' , // w 0x17 -> LATIN SMALL LETTER W
+ 'x' , // x 0x18 -> LATIN SMALL LETTER X
+ 'y' , // y 0x19 -> LATIN SMALL LETTER Y
+ 'z' , // z 0x1A -> LATIN SMALL LETTER Z
+ '[' , // [ 0x1B -> LEFT SQUARE BRACKET
+ '\u00a3', // £ 0x1C -> POUND SIGN
+ ']' , // ] 0x1D -> RIGHT SQUARE BRACKET
+ '\u2191', // ↑ 0x1E -> UPWARDS ARROW
+ '\u2190', // ← 0x1F -> LEFTWARDS ARROW
+ ' ' , // 0x20 -> SPACE
+ '!' , // ! 0x21 -> EXCLAMATION MARK
+ '"' , // " 0x22 -> QUOTATION MARK
+ '#' , // # 0x23 -> NUMBER SIGN
+ '$' , // $ 0x24 -> DOLLAR SIGN
+ '%' , // % 0x25 -> PERCENT SIGN
+ '&' , // & 0x26 -> AMPERSAND
+ '\'' , // ' 0x27 -> APOSTROPHE
+ '(' , // ( 0x28 -> LEFT PARENTHESIS
+ ')' , // ) 0x29 -> RIGHT PARENTHESIS
+ '*' , // * 0x2A -> ASTERISK
+ '+' , // + 0x2B -> PLUS SIGN
+ ',' , // , 0x2C -> COMMA
+ '-' , // - 0x2D -> HYPHEN-MINUS
+ '.' , // . 0x2E -> FULL STOP
+ '/' , // / 0x2F -> SOLIDUS
+ '0' , // 0 0x30 -> DIGIT ZERO
+ '1' , // 1 0x31 -> DIGIT ONE
+ '2' , // 2 0x32 -> DIGIT TWO
+ '3' , // 3 0x33 -> DIGIT THREE
+ '4' , // 4 0x34 -> DIGIT FOUR
+ '5' , // 5 0x35 -> DIGIT FIVE
+ '6' , // 6 0x36 -> DIGIT SIX
+ '7' , // 7 0x37 -> DIGIT SEVEN
+ '8' , // 8 0x38 -> DIGIT EIGHT
+ '9' , // 9 0x39 -> DIGIT NINE
+ ':' , // : 0x3A -> COLON
+ ';' , // ; 0x3B -> SEMICOLON
+ '<' , // < 0x3C -> LESS-THAN SIGN
+ '=' , // = 0x3D -> EQUALS SIGN
+ '>' , // > 0x3E -> GREATER-THAN SIGN
+ '?' , // ? 0x3F -> QUESTION MARK
+ '\u2500', // ─ 0x40 -> BOX DRAWINGS LIGHT HORIZONTAL
+ 'A' , // A 0x41 -> LATIN CAPITAL LETTER A
+ 'B' , // B 0x42 -> LATIN CAPITAL LETTER B
+ 'C' , // C 0x43 -> LATIN CAPITAL LETTER C
+ 'D' , // D 0x44 -> LATIN CAPITAL LETTER D
+ 'E' , // E 0x45 -> LATIN CAPITAL LETTER E
+ 'F' , // F 0x46 -> LATIN CAPITAL LETTER F
+ 'G' , // G 0x47 -> LATIN CAPITAL LETTER G
+ 'H' , // H 0x48 -> LATIN CAPITAL LETTER H
+ 'I' , // I 0x49 -> LATIN CAPITAL LETTER I
+ 'J' , // J 0x4A -> LATIN CAPITAL LETTER J
+ 'K' , // K 0x4B -> LATIN CAPITAL LETTER K
+ 'L' , // L 0x4C -> LATIN CAPITAL LETTER L
+ 'M' , // M 0x4D -> LATIN CAPITAL LETTER M
+ 'N' , // N 0x4E -> LATIN CAPITAL LETTER N
+ 'O' , // O 0x4F -> LATIN CAPITAL LETTER O
+ 'P' , // P 0x50 -> LATIN CAPITAL LETTER P
+ 'Q' , // Q 0x51 -> LATIN CAPITAL LETTER Q
+ 'R' , // R 0x52 -> LATIN CAPITAL LETTER R
+ 'S' , // S 0x53 -> LATIN CAPITAL LETTER S
+ 'T' , // T 0x54 -> LATIN CAPITAL LETTER T
+ 'U' , // U 0x55 -> LATIN CAPITAL LETTER U
+ 'V' , // V 0x56 -> LATIN CAPITAL LETTER V
+ 'W' , // W 0x57 -> LATIN CAPITAL LETTER W
+ 'X' , // X 0x58 -> LATIN CAPITAL LETTER X
+ 'Y' , // Y 0x59 -> LATIN CAPITAL LETTER Y
+ 'Z' , // Z 0x5A -> LATIN CAPITAL LETTER Z
+ '\u253c', // ┼ 0x5B -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ '\uf12e', // 0x5C -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
+ '\u2502', // │ 0x5D -> BOX DRAWINGS LIGHT VERTICAL
+ '\u2592', // ▒ 0x5E -> MEDIUM SHADE
+ '\uf139', // 0x5F -> MEDIUM SHADE SLASHED LEFT (CUS)
+ '\u00a0', // 0x60 -> NO-BREAK SPACE
+ '\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
+ '\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
+ '\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
+ '\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
+ '\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
+ '\u2592', // ▒ 0x66 -> MEDIUM SHADE
+ '\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
+ '\uf12f', // 0x68 -> LOWER HALF BLOCK MEDIUM SHADE (CUS)
+ '\uf13a', // 0x69 -> MEDIUM SHADE SLASHED RIGHT (CUS)
+ '\uf130', // 0x6A -> RIGHT ONE QUARTER BLOCK (CUS)
+ '\u251c', // ├ 0x6B -> BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ '\u2597', // ▗ 0x6C -> QUADRANT LOWER RIGHT
+ '\u2514', // └ 0x6D -> BOX DRAWINGS LIGHT UP AND RIGHT
+ '\u2510', // ┐ 0x6E -> BOX DRAWINGS LIGHT DOWN AND LEFT
+ '\u2582', // ▂ 0x6F -> LOWER ONE QUARTER BLOCK
+ '\u250c', // ┌ 0x70 -> BOX DRAWINGS LIGHT DOWN AND RIGHT
+ '\u2534', // ┴ 0x71 -> BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ '\u252c', // ┬ 0x72 -> BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ '\u2524', // ┤ 0x73 -> BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ '\u258e', // ▎ 0x74 -> LEFT ONE QUARTER BLOCK
+ '\u258d', // ▍ 0x75 -> LEFT THREE EIGTHS BLOCK
+ '\uf131', // 0x76 -> RIGHT THREE EIGHTHS BLOCK (CUS)
+ '\uf132', // 0x77 -> UPPER ONE QUARTER BLOCK (CUS)
+ '\uf133', // 0x78 -> UPPER THREE EIGHTS BLOCK (CUS)
+ '\u2583', // ▃ 0x79 -> LOWER THREE EIGHTHS BLOCK
+ '\u2713', // ✓ 0x7A -> CHECK MARK
+ '\u2596', // ▖ 0x7B -> QUADRANT LOWER LEFT
+ '\u259d', // ▝ 0x7C -> QUADRANT UPPER RIGHT
+ '\u2518', // ┘ 0x7D -> BOX DRAWINGS LIGHT UP AND LEFT
+ '\u2598', // ▘ 0x7E -> QUADRANT UPPER LEFT
+ '\u259a', // ▚ 0x7F -> QUADRANT UPPER LEFT AND LOWER RIGHT
+ '\ufffe', // 0x80 -> UNDEFINED
+ '\ufffe', // 0x81 -> UNDEFINED
+ '\ufffe', // 0x82 -> UNDEFINED
+ '\ufffe', // 0x83 -> UNDEFINED
+ '\ufffe', // 0x84 -> UNDEFINED
+ '\ufffe', // 0x85 -> UNDEFINED
+ '\ufffe', // 0x86 -> UNDEFINED
+ '\ufffe', // 0x87 -> UNDEFINED
+ '\ufffe', // 0x88 -> UNDEFINED
+ '\ufffe', // 0x89 -> UNDEFINED
+ '\ufffe', // 0x8A -> UNDEFINED
+ '\ufffe', // 0x8B -> UNDEFINED
+ '\ufffe', // 0x8C -> UNDEFINED
+ '\ufffe', // 0x8D -> UNDEFINED
+ '\ufffe', // 0x8E -> UNDEFINED
+ '\ufffe', // 0x8F -> UNDEFINED
+ '\ufffe', // 0x90 -> UNDEFINED
+ '\ufffe', // 0x91 -> UNDEFINED
+ '\ufffe', // 0x92 -> UNDEFINED
+ '\ufffe', // 0x93 -> UNDEFINED
+ '\ufffe', // 0x94 -> UNDEFINED
+ '\ufffe', // 0x95 -> UNDEFINED
+ '\ufffe', // 0x96 -> UNDEFINED
+ '\ufffe', // 0x97 -> UNDEFINED
+ '\ufffe', // 0x98 -> UNDEFINED
+ '\ufffe', // 0x99 -> UNDEFINED
+ '\ufffe', // 0x9A -> UNDEFINED
+ '\ufffe', // 0x9B -> UNDEFINED
+ '\ufffe', // 0x9C -> UNDEFINED
+ '\ufffe', // 0x9D -> UNDEFINED
+ '\ufffe', // 0x9E -> UNDEFINED
+ '\ufffe', // 0x9F -> UNDEFINED
+ '\ufffe', // 0xA0 -> UNDEFINED
+ '\ufffe', // 0xA1 -> UNDEFINED
+ '\ufffe', // 0xA2 -> UNDEFINED
+ '\ufffe', // 0xA3 -> UNDEFINED
+ '\ufffe', // 0xA4 -> UNDEFINED
+ '\ufffe', // 0xA5 -> UNDEFINED
+ '\ufffe', // 0xA6 -> UNDEFINED
+ '\ufffe', // 0xA7 -> UNDEFINED
+ '\ufffe', // 0xA8 -> UNDEFINED
+ '\ufffe', // 0xA9 -> UNDEFINED
+ '\ufffe', // 0xAA -> UNDEFINED
+ '\ufffe', // 0xAB -> UNDEFINED
+ '\ufffe', // 0xAC -> UNDEFINED
+ '\ufffe', // 0xAD -> UNDEFINED
+ '\ufffe', // 0xAE -> UNDEFINED
+ '\ufffe', // 0xAF -> UNDEFINED
+ '\ufffe', // 0xB0 -> UNDEFINED
+ '\ufffe', // 0xB1 -> UNDEFINED
+ '\ufffe', // 0xB2 -> UNDEFINED
+ '\ufffe', // 0xB3 -> UNDEFINED
+ '\ufffe', // 0xB4 -> UNDEFINED
+ '\ufffe', // 0xB5 -> UNDEFINED
+ '\ufffe', // 0xB6 -> UNDEFINED
+ '\ufffe', // 0xB7 -> UNDEFINED
+ '\ufffe', // 0xB8 -> UNDEFINED
+ '\ufffe', // 0xB9 -> UNDEFINED
+ '\ufffe', // 0xBA -> UNDEFINED
+ '\ufffe', // 0xBB -> UNDEFINED
+ '\ufffe', // 0xBC -> UNDEFINED
+ '\ufffe', // 0xBD -> UNDEFINED
+ '\ufffe', // 0xBE -> UNDEFINED
+ '\ufffe', // 0xBF -> UNDEFINED
+ '\ufffe', // 0xC0 -> UNDEFINED
+ '\ufffe', // 0xC1 -> UNDEFINED
+ '\ufffe', // 0xC2 -> UNDEFINED
+ '\ufffe', // 0xC3 -> UNDEFINED
+ '\ufffe', // 0xC4 -> UNDEFINED
+ '\ufffe', // 0xC5 -> UNDEFINED
+ '\ufffe', // 0xC6 -> UNDEFINED
+ '\ufffe', // 0xC7 -> UNDEFINED
+ '\ufffe', // 0xC8 -> UNDEFINED
+ '\ufffe', // 0xC9 -> UNDEFINED
+ '\ufffe', // 0xCA -> UNDEFINED
+ '\ufffe', // 0xCB -> UNDEFINED
+ '\ufffe', // 0xCC -> UNDEFINED
+ '\ufffe', // 0xCD -> UNDEFINED
+ '\ufffe', // 0xCE -> UNDEFINED
+ '\ufffe', // 0xCF -> UNDEFINED
+ '\ufffe', // 0xD0 -> UNDEFINED
+ '\ufffe', // 0xD1 -> UNDEFINED
+ '\ufffe', // 0xD2 -> UNDEFINED
+ '\ufffe', // 0xD3 -> UNDEFINED
+ '\ufffe', // 0xD4 -> UNDEFINED
+ '\ufffe', // 0xD5 -> UNDEFINED
+ '\ufffe', // 0xD6 -> UNDEFINED
+ '\ufffe', // 0xD7 -> UNDEFINED
+ '\ufffe', // 0xD8 -> UNDEFINED
+ '\ufffe', // 0xD9 -> UNDEFINED
+ '\ufffe', // 0xDA -> UNDEFINED
+ '\ufffe', // 0xDB -> UNDEFINED
+ '\ufffe', // 0xDC -> UNDEFINED
+ '\ufffe', // 0xDD -> UNDEFINED
+ '\ufffe', // 0xDE -> UNDEFINED
+ '\ufffe', // 0xDF -> UNDEFINED
+ '\ufffe', // 0xE0 -> UNDEFINED
+ '\ufffe', // 0xE1 -> UNDEFINED
+ '\ufffe', // 0xE2 -> UNDEFINED
+ '\ufffe', // 0xE3 -> UNDEFINED
+ '\ufffe', // 0xE4 -> UNDEFINED
+ '\ufffe', // 0xE5 -> UNDEFINED
+ '\ufffe', // 0xE6 -> UNDEFINED
+ '\ufffe', // 0xE7 -> UNDEFINED
+ '\ufffe', // 0xE8 -> UNDEFINED
+ '\ufffe', // 0xE9 -> UNDEFINED
+ '\ufffe', // 0xEA -> UNDEFINED
+ '\ufffe', // 0xEB -> UNDEFINED
+ '\ufffe', // 0xEC -> UNDEFINED
+ '\ufffe', // 0xED -> UNDEFINED
+ '\ufffe', // 0xEE -> UNDEFINED
+ '\ufffe', // 0xEF -> UNDEFINED
+ '\ufffe', // 0xF0 -> UNDEFINED
+ '\ufffe', // 0xF1 -> UNDEFINED
+ '\ufffe', // 0xF2 -> UNDEFINED
+ '\ufffe', // 0xF3 -> UNDEFINED
+ '\ufffe', // 0xF4 -> UNDEFINED
+ '\ufffe', // 0xF5 -> UNDEFINED
+ '\ufffe', // 0xF6 -> UNDEFINED
+ '\ufffe', // 0xF7 -> UNDEFINED
+ '\ufffe', // 0xF8 -> UNDEFINED
+ '\ufffe', // 0xF9 -> UNDEFINED
+ '\ufffe', // 0xFA -> UNDEFINED
+ '\ufffe', // 0xFB -> UNDEFINED
+ '\ufffe', // 0xFC -> UNDEFINED
+ '\ufffe', // 0xFD -> UNDEFINED
+ '\ufffe', // 0xFE -> UNDEFINED
+ '\ufffe' // 0xFF -> UNDEFINED
+ )
+
+ private val decodingScreencodeUppercase = arrayOf(
+ '@' , // @ 0x00 -> COMMERCIAL AT
+ 'A' , // A 0x01 -> LATIN CAPITAL LETTER A
+ 'B' , // B 0x02 -> LATIN CAPITAL LETTER B
+ 'C' , // C 0x03 -> LATIN CAPITAL LETTER C
+ 'D' , // D 0x04 -> LATIN CAPITAL LETTER D
+ 'E' , // E 0x05 -> LATIN CAPITAL LETTER E
+ 'F' , // F 0x06 -> LATIN CAPITAL LETTER F
+ 'G' , // G 0x07 -> LATIN CAPITAL LETTER G
+ 'H' , // H 0x08 -> LATIN CAPITAL LETTER H
+ 'I' , // I 0x09 -> LATIN CAPITAL LETTER I
+ 'J' , // J 0x0A -> LATIN CAPITAL LETTER J
+ 'K' , // K 0x0B -> LATIN CAPITAL LETTER K
+ 'L' , // L 0x0C -> LATIN CAPITAL LETTER L
+ 'M' , // M 0x0D -> LATIN CAPITAL LETTER M
+ 'N' , // N 0x0E -> LATIN CAPITAL LETTER N
+ 'O' , // O 0x0F -> LATIN CAPITAL LETTER O
+ 'P' , // P 0x10 -> LATIN CAPITAL LETTER P
+ 'Q' , // Q 0x11 -> LATIN CAPITAL LETTER Q
+ 'R' , // R 0x12 -> LATIN CAPITAL LETTER R
+ 'S' , // S 0x13 -> LATIN CAPITAL LETTER S
+ 'T' , // T 0x14 -> LATIN CAPITAL LETTER T
+ 'U' , // U 0x15 -> LATIN CAPITAL LETTER U
+ 'V' , // V 0x16 -> LATIN CAPITAL LETTER V
+ 'W' , // W 0x17 -> LATIN CAPITAL LETTER W
+ 'X' , // X 0x18 -> LATIN CAPITAL LETTER X
+ 'Y' , // Y 0x19 -> LATIN CAPITAL LETTER Y
+ 'Z' , // Z 0x1A -> LATIN CAPITAL LETTER Z
+ '[' , // [ 0x1B -> LEFT SQUARE BRACKET
+ '\u00a3', // £ 0x1C -> POUND SIGN
+ ']' , // ] 0x1D -> RIGHT SQUARE BRACKET
+ '\u2191', // ↑ 0x1E -> UPWARDS ARROW
+ '\u2190', // ← 0x1F -> LEFTWARDS ARROW
+ ' ' , // 0x20 -> SPACE
+ '!' , // ! 0x21 -> EXCLAMATION MARK
+ '"' , // " 0x22 -> QUOTATION MARK
+ '#' , // # 0x23 -> NUMBER SIGN
+ '$' , // $ 0x24 -> DOLLAR SIGN
+ '%' , // % 0x25 -> PERCENT SIGN
+ '&' , // & 0x26 -> AMPERSAND
+ '\'' , // ' 0x27 -> APOSTROPHE
+ '(' , // ( 0x28 -> LEFT PARENTHESIS
+ ')' , // ) 0x29 -> RIGHT PARENTHESIS
+ '*' , // * 0x2A -> ASTERISK
+ '+' , // + 0x2B -> PLUS SIGN
+ ',' , // , 0x2C -> COMMA
+ '-' , // - 0x2D -> HYPHEN-MINUS
+ '.' , // . 0x2E -> FULL STOP
+ '/' , // / 0x2F -> SOLIDUS
+ '0' , // 0 0x30 -> DIGIT ZERO
+ '1' , // 1 0x31 -> DIGIT ONE
+ '2' , // 2 0x32 -> DIGIT TWO
+ '3' , // 3 0x33 -> DIGIT THREE
+ '4' , // 4 0x34 -> DIGIT FOUR
+ '5' , // 5 0x35 -> DIGIT FIVE
+ '6' , // 6 0x36 -> DIGIT SIX
+ '7' , // 7 0x37 -> DIGIT SEVEN
+ '8' , // 8 0x38 -> DIGIT EIGHT
+ '9' , // 9 0x39 -> DIGIT NINE
+ ':' , // : 0x3A -> COLON
+ ';' , // ; 0x3B -> SEMICOLON
+ '<' , // < 0x3C -> LESS-THAN SIGN
+ '=' , // = 0x3D -> EQUALS SIGN
+ '>' , // > 0x3E -> GREATER-THAN SIGN
+ '?' , // ? 0x3F -> QUESTION MARK
+ '\u2500', // ─ 0x40 -> BOX DRAWINGS LIGHT HORIZONTAL
+ '\u2660', // ♠ 0x41 -> BLACK SPADE SUIT
+ '\u2502', // │ 0x42 -> BOX DRAWINGS LIGHT VERTICAL
+ '\u2500', // ─ 0x43 -> BOX DRAWINGS LIGHT HORIZONTAL
+ '\uf122', // 0x44 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER UP (CUS)
+ '\uf123', // 0x45 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS UP (CUS)
+ '\uf124', // 0x46 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER DOWN (CUS)
+ '\uf126', // 0x47 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER LEFT (CUS)
+ '\uf128', // 0x48 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER RIGHT (CUS)
+ '\u256e', // ╮ 0x49 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
+ '\u2570', // ╰ 0x4A -> BOX DRAWINGS LIGHT ARC UP AND RIGHT
+ '\u256f', // ╯ 0x4B -> BOX DRAWINGS LIGHT ARC UP AND LEFT
+ '\uf12a', // 0x4C -> ONE EIGHTH BLOCK UP AND RIGHT (CUS)
+ '\u2572', // ╲ 0x4D -> BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT
+ '\u2571', // ╱ 0x4E -> BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
+ '\uf12b', // 0x4F -> ONE EIGHTH BLOCK DOWN AND RIGHT (CUS)
+ '\uf12c', // 0x50 -> ONE EIGHTH BLOCK DOWN AND LEFT (CUS)
+ '\u25cf', // ● 0x51 -> BLACK CIRCLE
+ '\uf125', // 0x52 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS DOWN (CUS)
+ '\u2665', // ♥ 0x53 -> BLACK HEART SUIT
+ '\uf127', // 0x54 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS LEFT (CUS)
+ '\u256d', // ╭ 0x55 -> BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
+ '\u2573', // ╳ 0x56 -> BOX DRAWINGS LIGHT DIAGONAL CROSS
+ '\u25cb', // ○ 0x57 -> WHITE CIRCLE
+ '\u2663', // ♣ 0x58 -> BLACK CLUB SUIT
+ '\uf129', // 0x59 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS RIGHT (CUS)
+ '\u2666', // ♦ 0x5A -> BLACK DIAMOND SUIT
+ '\u253c', // ┼ 0x5B -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
+ '\uf12e', // 0x5C -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
+ '\u2502', // │ 0x5D -> BOX DRAWINGS LIGHT VERTICAL
+ '\u03c0', // π 0x5E -> GREEK SMALL LETTER PI
+ '\u25e5', // ◥ 0x5F -> BLACK UPPER RIGHT TRIANGLE
+ '\u00a0', // 0x60 -> NO-BREAK SPACE
+ '\u258c', // ▌ 0x61 -> LEFT HALF BLOCK
+ '\u2584', // ▄ 0x62 -> LOWER HALF BLOCK
+ '\u2594', // ▔ 0x63 -> UPPER ONE EIGHTH BLOCK
+ '\u2581', // ▁ 0x64 -> LOWER ONE EIGHTH BLOCK
+ '\u258f', // ▏ 0x65 -> LEFT ONE EIGHTH BLOCK
+ '\u2592', // ▒ 0x66 -> MEDIUM SHADE
+ '\u2595', // ▕ 0x67 -> RIGHT ONE EIGHTH BLOCK
+ '\uf12f', // 0x68 -> LOWER HALF BLOCK MEDIUM SHADE (CUS)
+ '\u25e4', // ◤ 0x69 -> BLACK UPPER LEFT TRIANGLE
+ '\uf130', // 0x6A -> RIGHT ONE QUARTER BLOCK (CUS)
+ '\u251c', // ├ 0x6B -> BOX DRAWINGS LIGHT VERTICAL AND RIGHT
+ '\u2597', // ▗ 0x6C -> QUADRANT LOWER RIGHT
+ '\u2514', // └ 0x6D -> BOX DRAWINGS LIGHT UP AND RIGHT
+ '\u2510', // ┐ 0x6E -> BOX DRAWINGS LIGHT DOWN AND LEFT
+ '\u2582', // ▂ 0x6F -> LOWER ONE QUARTER BLOCK
+ '\u250c', // ┌ 0x70 -> BOX DRAWINGS LIGHT DOWN AND RIGHT
+ '\u2534', // ┴ 0x71 -> BOX DRAWINGS LIGHT UP AND HORIZONTAL
+ '\u252c', // ┬ 0x72 -> BOX DRAWINGS LIGHT DOWN AND HORIZONTAL
+ '\u2524', // ┤ 0x73 -> BOX DRAWINGS LIGHT VERTICAL AND LEFT
+ '\u258e', // ▎ 0x74 -> LEFT ONE QUARTER BLOCK
+ '\u258d', // ▍ 0x75 -> LEFT THREE EIGTHS BLOCK
+ '\uf131', // 0x76 -> RIGHT THREE EIGHTHS BLOCK (CUS)
+ '\uf132', // 0x77 -> UPPER ONE QUARTER BLOCK (CUS)
+ '\uf133', // 0x78 -> UPPER THREE EIGHTS BLOCK (CUS)
+ '\u2583', // ▃ 0x79 -> LOWER THREE EIGHTHS BLOCK
+ '\uf12d', // 0x7A -> ONE EIGHTH BLOCK UP AND LEFT (CUS)
+ '\u2596', // ▖ 0x7B -> QUADRANT LOWER LEFT
+ '\u259d', // ▝ 0x7C -> QUADRANT UPPER RIGHT
+ '\u2518', // ┘ 0x7D -> BOX DRAWINGS LIGHT UP AND LEFT
+ '\u2598', // ▘ 0x7E -> QUADRANT UPPER LEFT
+ '\u259a', // ▚ 0x7F -> QUADRANT UPPER LEFT AND LOWER RIGHT
+ '\ufffe', // 0x80 -> UNDEFINED
+ '\ufffe', // 0x81 -> UNDEFINED
+ '\ufffe', // 0x82 -> UNDEFINED
+ '\ufffe', // 0x83 -> UNDEFINED
+ '\ufffe', // 0x84 -> UNDEFINED
+ '\ufffe', // 0x85 -> UNDEFINED
+ '\ufffe', // 0x86 -> UNDEFINED
+ '\ufffe', // 0x87 -> UNDEFINED
+ '\ufffe', // 0x88 -> UNDEFINED
+ '\ufffe', // 0x89 -> UNDEFINED
+ '\ufffe', // 0x8A -> UNDEFINED
+ '\ufffe', // 0x8B -> UNDEFINED
+ '\ufffe', // 0x8C -> UNDEFINED
+ '\ufffe', // 0x8D -> UNDEFINED
+ '\ufffe', // 0x8E -> UNDEFINED
+ '\ufffe', // 0x8F -> UNDEFINED
+ '\ufffe', // 0x90 -> UNDEFINED
+ '\ufffe', // 0x91 -> UNDEFINED
+ '\ufffe', // 0x92 -> UNDEFINED
+ '\ufffe', // 0x93 -> UNDEFINED
+ '\ufffe', // 0x94 -> UNDEFINED
+ '\ufffe', // 0x95 -> UNDEFINED
+ '\ufffe', // 0x96 -> UNDEFINED
+ '\ufffe', // 0x97 -> UNDEFINED
+ '\ufffe', // 0x98 -> UNDEFINED
+ '\ufffe', // 0x99 -> UNDEFINED
+ '\ufffe', // 0x9A -> UNDEFINED
+ '\ufffe', // 0x9B -> UNDEFINED
+ '\ufffe', // 0x9C -> UNDEFINED
+ '\ufffe', // 0x9D -> UNDEFINED
+ '\ufffe', // 0x9E -> UNDEFINED
+ '\ufffe', // 0x9F -> UNDEFINED
+ '\ufffe', // 0xA0 -> UNDEFINED
+ '\ufffe', // 0xA1 -> UNDEFINED
+ '\ufffe', // 0xA2 -> UNDEFINED
+ '\ufffe', // 0xA3 -> UNDEFINED
+ '\ufffe', // 0xA4 -> UNDEFINED
+ '\ufffe', // 0xA5 -> UNDEFINED
+ '\ufffe', // 0xA6 -> UNDEFINED
+ '\ufffe', // 0xA7 -> UNDEFINED
+ '\ufffe', // 0xA8 -> UNDEFINED
+ '\ufffe', // 0xA9 -> UNDEFINED
+ '\ufffe', // 0xAA -> UNDEFINED
+ '\ufffe', // 0xAB -> UNDEFINED
+ '\ufffe', // 0xAC -> UNDEFINED
+ '\ufffe', // 0xAD -> UNDEFINED
+ '\ufffe', // 0xAE -> UNDEFINED
+ '\ufffe', // 0xAF -> UNDEFINED
+ '\ufffe', // 0xB0 -> UNDEFINED
+ '\ufffe', // 0xB1 -> UNDEFINED
+ '\ufffe', // 0xB2 -> UNDEFINED
+ '\ufffe', // 0xB3 -> UNDEFINED
+ '\ufffe', // 0xB4 -> UNDEFINED
+ '\ufffe', // 0xB5 -> UNDEFINED
+ '\ufffe', // 0xB6 -> UNDEFINED
+ '\ufffe', // 0xB7 -> UNDEFINED
+ '\ufffe', // 0xB8 -> UNDEFINED
+ '\ufffe', // 0xB9 -> UNDEFINED
+ '\ufffe', // 0xBA -> UNDEFINED
+ '\ufffe', // 0xBB -> UNDEFINED
+ '\ufffe', // 0xBC -> UNDEFINED
+ '\ufffe', // 0xBD -> UNDEFINED
+ '\ufffe', // 0xBE -> UNDEFINED
+ '\ufffe', // 0xBF -> UNDEFINED
+ '\ufffe', // 0xC0 -> UNDEFINED
+ '\ufffe', // 0xC1 -> UNDEFINED
+ '\ufffe', // 0xC2 -> UNDEFINED
+ '\ufffe', // 0xC3 -> UNDEFINED
+ '\ufffe', // 0xC4 -> UNDEFINED
+ '\ufffe', // 0xC5 -> UNDEFINED
+ '\ufffe', // 0xC6 -> UNDEFINED
+ '\ufffe', // 0xC7 -> UNDEFINED
+ '\ufffe', // 0xC8 -> UNDEFINED
+ '\ufffe', // 0xC9 -> UNDEFINED
+ '\ufffe', // 0xCA -> UNDEFINED
+ '\ufffe', // 0xCB -> UNDEFINED
+ '\ufffe', // 0xCC -> UNDEFINED
+ '\ufffe', // 0xCD -> UNDEFINED
+ '\ufffe', // 0xCE -> UNDEFINED
+ '\ufffe', // 0xCF -> UNDEFINED
+ '\ufffe', // 0xD0 -> UNDEFINED
+ '\ufffe', // 0xD1 -> UNDEFINED
+ '\ufffe', // 0xD2 -> UNDEFINED
+ '\ufffe', // 0xD3 -> UNDEFINED
+ '\ufffe', // 0xD4 -> UNDEFINED
+ '\ufffe', // 0xD5 -> UNDEFINED
+ '\ufffe', // 0xD6 -> UNDEFINED
+ '\ufffe', // 0xD7 -> UNDEFINED
+ '\ufffe', // 0xD8 -> UNDEFINED
+ '\ufffe', // 0xD9 -> UNDEFINED
+ '\ufffe', // 0xDA -> UNDEFINED
+ '\ufffe', // 0xDB -> UNDEFINED
+ '\ufffe', // 0xDC -> UNDEFINED
+ '\ufffe', // 0xDD -> UNDEFINED
+ '\ufffe', // 0xDE -> UNDEFINED
+ '\ufffe', // 0xDF -> UNDEFINED
+ '\ufffe', // 0xE0 -> UNDEFINED
+ '\ufffe', // 0xE1 -> UNDEFINED
+ '\ufffe', // 0xE2 -> UNDEFINED
+ '\ufffe', // 0xE3 -> UNDEFINED
+ '\ufffe', // 0xE4 -> UNDEFINED
+ '\ufffe', // 0xE5 -> UNDEFINED
+ '\ufffe', // 0xE6 -> UNDEFINED
+ '\ufffe', // 0xE7 -> UNDEFINED
+ '\ufffe', // 0xE8 -> UNDEFINED
+ '\ufffe', // 0xE9 -> UNDEFINED
+ '\ufffe', // 0xEA -> UNDEFINED
+ '\ufffe', // 0xEB -> UNDEFINED
+ '\ufffe', // 0xEC -> UNDEFINED
+ '\ufffe', // 0xED -> UNDEFINED
+ '\ufffe', // 0xEE -> UNDEFINED
+ '\ufffe', // 0xEF -> UNDEFINED
+ '\ufffe', // 0xF0 -> UNDEFINED
+ '\ufffe', // 0xF1 -> UNDEFINED
+ '\ufffe', // 0xF2 -> UNDEFINED
+ '\ufffe', // 0xF3 -> UNDEFINED
+ '\ufffe', // 0xF4 -> UNDEFINED
+ '\ufffe', // 0xF5 -> UNDEFINED
+ '\ufffe', // 0xF6 -> UNDEFINED
+ '\ufffe', // 0xF7 -> UNDEFINED
+ '\ufffe', // 0xF8 -> UNDEFINED
+ '\ufffe', // 0xF9 -> UNDEFINED
+ '\ufffe', // 0xFA -> UNDEFINED
+ '\ufffe', // 0xFB -> UNDEFINED
+ '\ufffe', // 0xFC -> UNDEFINED
+ '\ufffe', // 0xFD -> UNDEFINED
+ '\ufffe', // 0xFE -> UNDEFINED
+ '\ufffe' // 0xFF -> UNDEFINED
+ )
+
+ // encoding: from unicode to Screencodes (0-255)
+ private val encodingScreencodeLowercase = decodingScreencodeLowercase.withIndex().associate{it.value to it.index}
+ private val encodingScreencodeUppercase = decodingScreencodeUppercase.withIndex().associate{it.value to it.index}
+
+
+ fun encodeScreencode(text: String, lowercase: Boolean = false): List {
+ val lookup = if(lowercase) encodingScreencodeLowercase else encodingScreencodeUppercase
+ return text.map{
+ val screencode = lookup[it]
+ screencode?.toShort() ?: if(it=='\u0000')
+ 0.toShort()
+ else {
+ val case = if (lowercase) "lower" else "upper"
+ throw CharConversionException("no ${case}Screencode character for '$it'")
+ }
+ }
+ }
+
+ fun decodeScreencode(screencode: Iterable, lowercase: Boolean = false): String {
+ val decodeTable = if(lowercase) decodingScreencodeLowercase else decodingScreencodeUppercase
+ return screencode.map { decodeTable[it.toInt()] }.joinToString("")
+ }
+}
diff --git a/sim65/src/Sim65Main.kt b/sim65/src/Sim65Main.kt
new file mode 100644
index 000000000..7c2db9a0c
--- /dev/null
+++ b/sim65/src/Sim65Main.kt
@@ -0,0 +1,88 @@
+package sim65
+
+import kotlinx.cli.*
+import sim65.components.*
+import sim65.components.Cpu6502.Companion.hexB
+import kotlin.system.exitProcess
+
+
+fun main(args: Array) {
+ printSoftwareHeader()
+ startSimulator(args)
+}
+
+internal fun printSoftwareHeader() {
+ val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
+ println("\nSim65 6502 cpu simulator v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
+ println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
+}
+
+
+private fun startSimulator(args: Array) {
+ val cli = CommandLineInterface("sim65", printHelpByDefault = false)
+ val enableIllegal by cli.flagArgument("-ill", "enable the illegal instructions")
+
+ try {
+ cli.parse(args)
+ } catch (e: Exception) {
+ exitProcess(1)
+ }
+
+ val bootRom = listOf(
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,
+ 0x00,0x90, // NMI vector
+ 0x00,0x10, // RESET vector
+ 0x00,0xa0 // IRQ vector
+ ).toTypedArray()
+
+ val cpu = Cpu6502(enableIllegal)
+ cpu.tracing = true
+
+ // create the system bus and add device to it.
+ // note that the order is relevant w.r.t. where reads and writes are going.
+ val bus = Bus()
+ bus.add(cpu)
+ bus.add(Rom(0xff00, 0xffff, bootRom))
+ bus.add(Parallel(0xd000, 0xd001))
+ bus.add(Timer(0xd100, 0xd103))
+ val ram = Ram(0, 0xffff)
+ bus.add(ram)
+
+ bus.reset()
+
+ ram.load("sim65/ram.bin", 0x8000)
+ ram.load("sim65/bcdtest.bin", 0x1000)
+ //ram.dump(0x8000, 0x802f)
+ //cpu.disassemble(ram, 0x8000, 0x802f)
+
+ while(true) {
+ bus.clock()
+ if(cpu.totalCycles > 300)
+ break
+ }
+
+ if(ram.read(0x0400)==0.toShort())
+ println("BCD TEST: OK!")
+ else {
+ val v1 = ram.read(0x0401)
+ val v2 = ram.read(0x0402)
+ println("BCD TEST: FAIL!! value1=${hexB(v1)} value2=${hexB(v2)}")
+ }
+ ram.dump(0x0400, 0x0402)
+
+}
diff --git a/sim65/src/components/Bus.kt b/sim65/src/components/Bus.kt
new file mode 100644
index 000000000..b05a10683
--- /dev/null
+++ b/sim65/src/components/Bus.kt
@@ -0,0 +1,46 @@
+package sim65.components
+
+typealias UByte = Short
+typealias Address = Int
+
+
+class Bus {
+
+ private val components = mutableListOf()
+ private val memComponents = mutableListOf()
+
+ fun reset() {
+ components.forEach { it.reset() }
+ memComponents.forEach { it.reset() }
+ }
+
+ fun clock() {
+ components.forEach { it.clock() }
+ memComponents.forEach { it.clock() }
+ }
+
+ fun add(component: BusComponent) {
+ components.add(component)
+ component.bus = this
+ }
+
+ fun add(component: MemMappedComponent) {
+ memComponents.add(component)
+ component.bus = this
+ }
+
+ fun read(address: Address): UByte {
+ memComponents.forEach {
+ if(address>=it.startAddress && address<=it.endAddress)
+ return it.read(address)
+ }
+ return 0xff
+ }
+
+ fun write(address: Address, data: UByte) {
+ memComponents.forEach {
+ if(address>=it.startAddress && address<=it.endAddress)
+ it.write(address, data)
+ }
+ }
+}
diff --git a/sim65/src/components/Component.kt b/sim65/src/components/Component.kt
new file mode 100644
index 000000000..a0d01071e
--- /dev/null
+++ b/sim65/src/components/Component.kt
@@ -0,0 +1,35 @@
+package sim65.components
+
+import sim65.C64Screencodes
+
+abstract class BusComponent {
+ lateinit var bus: Bus
+
+ abstract fun clock()
+ abstract fun reset()
+}
+
+abstract class MemMappedComponent(val startAddress: Address, val endAddress: Address): BusComponent() {
+ abstract fun read(address: Address): UByte
+ abstract fun write(address: Address, data: UByte)
+ abstract fun cloneMem(): Array
+
+ init {
+ require(endAddress>=startAddress)
+ require(startAddress>=0 && endAddress <= 0xffff) { "can only have 16-bit address space" }
+ }
+
+ fun dump(from: Address, to: Address) {
+ (from .. to).chunked(16).forEach {
+ print("\$${it.first().toString(16).padStart(4, '0')} ")
+ val bytes = it.map { address -> read(address) }
+ bytes.forEach { byte ->
+ print(byte.toString(16).padStart(2, '0') + " ")
+ }
+ print(" ")
+ print(C64Screencodes.decodeScreencode(bytes, false).replace('\ufffe', '.'))
+ println()
+ }
+ }
+
+}
diff --git a/sim65/src/components/Cpu6502.kt b/sim65/src/components/Cpu6502.kt
new file mode 100644
index 000000000..ee5bbce38
--- /dev/null
+++ b/sim65/src/components/Cpu6502.kt
@@ -0,0 +1,1138 @@
+package sim65.components
+
+
+class InstructionError(msg: String) : RuntimeException(msg)
+
+interface ICpu {
+ fun disassemble(memory: Array, baseAddress: Address, from: Address, to: Address)
+ fun disassemble(component: MemMappedComponent, from: Address, to: Address) =
+ disassemble(component.cloneMem(), component.startAddress, from, to)
+
+ fun dumpState()
+ fun clock()
+ fun reset()
+
+ var tracing: Boolean
+ val totalCycles: Int
+}
+
+// TODO: add additional cycles to certain instructions and addressing modes
+// TODO: test all opcodes and fix bugs
+
+
+class Cpu6502(private val illegalInstrsAllowed: Boolean) : BusComponent(), ICpu {
+ override var tracing: Boolean = false
+ override var totalCycles: Int = 0
+ private set
+
+ companion object {
+ const val NMI_vector = 0xfffa
+ const val RESET_vector = 0xfffc
+ const val IRQ_vector = 0xfffe
+
+ fun hexW(number: Address): String {
+ val msb = number ushr 8
+ val lsb = number and 255
+ return hexB(msb) + hexB(lsb)
+ }
+
+ private const val hexdigits = "0123456789abcdef"
+
+ fun hexB(number: Short): String = hexB(number.toInt())
+
+ fun hexB(number: Int): String {
+ val loNibble = number and 15
+ val hiNibble = number ushr 4
+ return hexdigits[hiNibble].toString() + hexdigits[loNibble]
+ }
+ }
+
+ enum class AddrMode {
+ Imp,
+ Acc,
+ Imm,
+ Zp,
+ ZpX,
+ ZpY,
+ Rel,
+ Abs,
+ AbsX,
+ AbsY,
+ Ind,
+ IzX,
+ IzY
+ }
+
+ class Instruction(val mnemonic: String, val mode: AddrMode, val cycles: Int, val official: Boolean, val execute: () -> Unit)
+
+ class StatusRegister(
+ var C: Boolean = false,
+ var Z: Boolean = false,
+ var I: Boolean = false,
+ var D: Boolean = false,
+ var B: Boolean = false,
+ var V: Boolean = false,
+ var N: Boolean = false
+ ) {
+ fun asByte(): UByte {
+ return (0b00100000 or
+ (if (N) 0b10000000 else 0) or
+ (if (V) 0b01000000 else 0) or
+ (if (B) 0b00010000 else 0) or
+ (if (D) 0b00001000 else 0) or
+ (if (I) 0b00000100 else 0) or
+ (if (Z) 0b00000010 else 0) or
+ (if (C) 0b00000001 else 0)
+ ).toShort()
+ }
+
+ fun fromByte(byte: Int) {
+ N = (byte and 0b10000000) != 0
+ V = (byte and 0b01000000) != 0
+ B = (byte and 0b00010000) != 0
+ D = (byte and 0b00001000) != 0
+ I = (byte and 0b00000100) != 0
+ Z = (byte and 0b00000010) != 0
+ C = (byte and 0b00000001) != 0
+ }
+ }
+
+
+ private var instrCycles: Int = 0
+ private var A: Int = 0
+ private var X: Int = 0
+ private var Y: Int = 0
+ private var sp: Int = 0
+ private var pc: Address = 0
+ private val status = StatusRegister()
+ private var currentOpcode: Int = 0
+ private lateinit var currentInstruction: Instruction
+
+ // data byte from the instruction (only set when addr.mode is Accumulator, Immediate or Implied)
+ private var fetchedData: Int = 0
+
+ // all other addressing modes yield a fetched memory address
+ private var fetchedAddress: Address = 0
+
+ private val addressingModes = mapOf Unit>(
+ AddrMode.Imp to ::amImp,
+ AddrMode.Acc to ::amAcc,
+ AddrMode.Imm to ::amImm,
+ AddrMode.Zp to ::amZp,
+ AddrMode.ZpX to ::amZpx,
+ AddrMode.ZpY to ::amZpy,
+ AddrMode.Rel to ::amRel,
+ AddrMode.Abs to ::amAbs,
+ AddrMode.AbsX to ::amAbsx,
+ AddrMode.AbsY to ::amAbsy,
+ AddrMode.Ind to ::amInd,
+ AddrMode.IzX to ::amIzx,
+ AddrMode.IzY to ::amIzy
+ )
+
+ override fun disassemble(memory: Array, baseAddress: Address, from: Address, to: Address) {
+ var address = from - baseAddress
+ val spacing1 = " "
+ val spacing2 = " "
+ val spacing3 = " "
+ while (address <= (to - baseAddress)) {
+ val byte = memory[address]
+ print("\$${hexW(address)} ${hexB(byte)} ")
+ address++
+ val opcode = opcodes[byte.toInt()]
+ if (!opcode.official && !illegalInstrsAllowed) {
+ println("$spacing1 ???")
+ } else {
+ when (opcode.mode) {
+ AddrMode.Imp, AddrMode.Acc -> {
+ println("$spacing1 ${opcode.mnemonic}")
+ }
+ AddrMode.Imm -> {
+ val value = memory[address++]
+ println("${hexB(value)} $spacing2 ${opcode.mnemonic} #\$${hexB(value)}")
+ }
+ AddrMode.Zp -> {
+ val zpAddr = memory[address++]
+ println("${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)}")
+ }
+ AddrMode.ZpX -> {
+ val zpAddr = memory[address++]
+ println("${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},x")
+ }
+ AddrMode.ZpY -> {
+ val zpAddr = memory[address++]
+ println("${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} \$${hexB(zpAddr)},y")
+ }
+ AddrMode.Rel -> {
+ val rel = memory[address++]
+ val target =
+ if (rel <= 0x7f)
+ address + rel
+ else
+ address - (256 - rel)
+ println("${hexB(rel)} $spacing2 ${opcode.mnemonic} \$${hexW(target)}")
+ }
+ AddrMode.Abs -> {
+ val lo = memory[address++]
+ val hi = memory[address++]
+ val absAddr = lo.toInt() or (hi.toInt() shl 8)
+ println("${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)}")
+ }
+ AddrMode.AbsX -> {
+ val lo = memory[address++]
+ val hi = memory[address++]
+ val absAddr = lo.toInt() or (hi.toInt() shl 8)
+ println("${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},x")
+ }
+ AddrMode.AbsY -> {
+ val lo = memory[address++]
+ val hi = memory[address++]
+ val absAddr = lo.toInt() or (hi.toInt() shl 8)
+ println("${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} \$${hexW(absAddr)},y")
+ }
+ AddrMode.Ind -> {
+ val lo = memory[address++]
+ val hi = memory[address++]
+ val indirectAddr = lo.toInt() or (hi.toInt() shl 8)
+ println("${hexB(lo)} ${hexB(hi)} $spacing3 ${opcode.mnemonic} (\$${hexW(indirectAddr)})")
+ }
+ AddrMode.IzX -> {
+ val zpAddr = memory[address++]
+ println("${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)},x)")
+ }
+ AddrMode.IzY -> {
+ val zpAddr = memory[address++]
+ println("${hexB(zpAddr)} $spacing2 ${opcode.mnemonic} (\$${hexB(zpAddr)}),y")
+ }
+ }
+ }
+ }
+ }
+
+ override fun reset() {
+ sp = 0xfd
+ pc = readWord(RESET_vector)
+ A = 0
+ X = 0
+ Y = 0
+ status.C = false
+ status.Z = false
+ status.I = false
+ status.D = false
+ status.B = false
+ status.V = false
+ status.N = false
+ instrCycles = 8
+ currentOpcode = 0
+ currentInstruction = opcodes[0]
+ }
+
+ override fun clock() {
+ if (instrCycles == 0) {
+ currentOpcode = read(pc++)
+ currentInstruction = opcodes[currentOpcode]
+
+ if (tracing) {
+ dumpState()
+ }
+
+ if (!currentInstruction.official && !illegalInstrsAllowed) {
+ throw InstructionError("illegal instructions not enabled")
+ }
+
+ instrCycles = currentInstruction.cycles
+ addressingModes.getValue(currentInstruction.mode)()
+ currentInstruction.execute()
+ }
+
+ instrCycles--
+ totalCycles++
+ }
+
+ override fun dumpState() {
+ println("cycle:$totalCycles - pc=${hexW(pc)} " +
+ "A=${hexB(A)} " +
+ "X=${hexB(X)} " +
+ "Y=${hexB(Y)} " +
+ "SP=${hexB(sp)} " +
+ " n=" + (if (status.N) "1" else "0") +
+ " v=" + (if (status.V) "1" else "0") +
+ " b=" + (if (status.B) "1" else "0") +
+ " d=" + (if (status.D) "1" else "0") +
+ " i=" + (if (status.I) "1" else "0") +
+ " z=" + (if (status.Z) "1" else "0") +
+ " c=" + (if (status.C) "1" else "0") +
+ " icycles=$instrCycles instr=${hexB(currentOpcode)}:${currentInstruction.mnemonic}"
+ )
+ }
+
+ private fun amImp() {
+ fetchedData = A
+ }
+
+ private fun amAcc() {
+ fetchedData = A
+ }
+
+ private fun amImm() {
+ fetchedData = readPc()
+ }
+
+ private fun amZp() {
+ fetchedAddress = readPc()
+ }
+
+ private fun amZpx() {
+ // note: zeropage index will not leave Zp when page boundray is crossed
+ fetchedAddress = (readPc() + X) and 255
+ }
+
+ private fun amZpy() {
+ // note: zeropage index will not leave Zp when page boundray is crossed
+ fetchedAddress = (readPc() + Y) and 255
+ }
+
+ private fun amRel() {
+ val relative = readPc().toByte()
+ fetchedAddress = if (relative >= 0x80)
+ pc - (256 - relative)
+ else
+ pc + relative
+ }
+
+ private fun amAbs() {
+ val lo = readPc()
+ val hi = readPc()
+ fetchedAddress = lo or (hi shl 8)
+ }
+
+ private fun amAbsx() {
+ val lo = readPc()
+ val hi = readPc()
+ fetchedAddress = (X + (lo or (hi shl 8))) and 0xffff
+ }
+
+ private fun amAbsy() {
+ val lo = readPc()
+ val hi = readPc()
+ fetchedAddress = (Y + (lo or (hi shl 8))) and 0xffff
+ }
+
+ private fun amInd() {
+ // not able to fetch an adress which crosses the page boundary (6502, fixed in 65C02)
+ var lo = readPc()
+ var hi = readPc()
+ fetchedAddress = lo or (hi shl 8)
+ if (lo == 0xff) {
+ // emulate bug
+ lo = read(fetchedAddress)
+ hi = read(fetchedAddress and 0xff00)
+ } else {
+ // normal behavior
+ lo = read(fetchedAddress)
+ hi = read(fetchedAddress + 1)
+ }
+ fetchedAddress = lo or (hi shl 8)
+ }
+
+ private fun amIzx() {
+ // note: not able to fetch an adress which crosses the page boundary
+ fetchedAddress = readPc() + X
+ val lo = read(fetchedAddress)
+ val hi = read((fetchedAddress + 1) and 255)
+ fetchedAddress = lo or (hi shl 8)
+ }
+
+ private fun amIzy() {
+ // note: not able to fetch an adress which crosses the page boundary
+ fetchedAddress = readPc()
+ val lo = read(fetchedAddress)
+ val hi = read((fetchedAddress + 1) and 255)
+ fetchedAddress = Y + (lo or (hi shl 8))
+ }
+
+ private fun readPc(): Int = bus.read(pc++).toInt()
+
+ private fun pushStackAddr(address: Address) {
+ val lo = address and 255
+ val hi = (address ushr 8)
+ pushStack(hi)
+ pushStack(lo)
+ }
+
+ private fun pushStack(status: StatusRegister) {
+ pushStack(status.asByte().toInt())
+ }
+
+ private fun pushStack(data: Int) {
+ write(sp or 0x0100, data)
+ sp = (sp - 1) and 0xff
+ }
+
+ private fun popStack(): Int {
+ sp = (sp + 1) and 0xff
+ return read(sp or 0x0100)
+ }
+
+ private fun popStackAddr(): Address {
+ val lo = popStack()
+ val hi = popStack()
+ return lo + hi ushr 8
+ }
+
+ private fun read(address: Address): Int = bus.read(address).toInt()
+ private fun readWord(address: Address): Int = bus.read(address).toInt() or (bus.read(address + 1).toInt() shl 8)
+ private fun write(address: Address, data: Int) = bus.write(address, data.toShort())
+
+ // opcodes table from http://www.oxyron.de/html/opcodes02.html
+ private val opcodes = listOf(
+ /* 00 */ Instruction("brk", AddrMode.Imp, 7, true, ::iBrk),
+ /* 01 */ Instruction("ora", AddrMode.IzX, 6, true, ::iOra),
+ /* 02 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 03 */ Instruction("slo", AddrMode.IzX, 8, false, ::iSlo),
+ /* 04 */ Instruction("nop", AddrMode.Zp, 3, false, ::iNop),
+ /* 05 */ Instruction("ora", AddrMode.Zp, 3, true, ::iOra),
+ /* 06 */ Instruction("asl", AddrMode.Zp, 5, true, ::iAsl),
+ /* 07 */ Instruction("slo", AddrMode.Zp, 5, false, ::iSlo),
+ /* 08 */ Instruction("php", AddrMode.Imp, 3, true, ::iPhp),
+ /* 09 */ Instruction("ora", AddrMode.Imm, 2, true, ::iOra),
+ /* 0a */ Instruction("asl", AddrMode.Acc, 2, true, ::iAsl),
+ /* 0b */ Instruction("anc", AddrMode.Imm, 2, false, ::iAnc),
+ /* 0c */ Instruction("nop", AddrMode.Abs, 4, false, ::iNop),
+ /* 0d */ Instruction("ora", AddrMode.Abs, 4, true, ::iOra),
+ /* 0e */ Instruction("asl", AddrMode.Abs, 6, true, ::iAsl),
+ /* 0f */ Instruction("slo", AddrMode.Abs, 6, false, ::iSlo),
+ /* 10 */ Instruction("bpl", AddrMode.Rel, 2, true, ::iBpl),
+ /* 11 */ Instruction("ora", AddrMode.IzY, 5, true, ::iOra),
+ /* 12 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 13 */ Instruction("slo", AddrMode.IzY, 6, false, ::iSlo),
+ /* 14 */ Instruction("nop", AddrMode.ZpX, 4, false, ::iNop),
+ /* 15 */ Instruction("ora", AddrMode.ZpX, 4, true, ::iOra),
+ /* 16 */ Instruction("asl", AddrMode.ZpX, 6, true, ::iAsl),
+ /* 17 */ Instruction("slo", AddrMode.ZpX, 6, false, ::iSlo),
+ /* 18 */ Instruction("clc", AddrMode.Imp, 2, true, ::iClc),
+ /* 19 */ Instruction("ora", AddrMode.AbsY, 4, true, ::iOra),
+ /* 1a */ Instruction("nop", AddrMode.Imp, 2, false, ::iNop),
+ /* 1b */ Instruction("slo", AddrMode.AbsY, 7, false, ::iSlo),
+ /* 1c */ Instruction("nop", AddrMode.AbsX, 4, false, ::iNop),
+ /* 1d */ Instruction("ora", AddrMode.AbsX, 4, true, ::iOra),
+ /* 1e */ Instruction("asl", AddrMode.AbsX, 7, true, ::iAsl),
+ /* 1f */ Instruction("slo", AddrMode.AbsX, 7, false, ::iSlo),
+ /* 20 */ Instruction("jsr", AddrMode.Abs, 6, true, ::iJsr),
+ /* 21 */ Instruction("and", AddrMode.IzX, 6, true, ::iAnd),
+ /* 22 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 23 */ Instruction("rla", AddrMode.IzX, 8, false, ::iRla),
+ /* 24 */ Instruction("bit", AddrMode.Zp, 3, true, ::iBit),
+ /* 25 */ Instruction("and", AddrMode.Zp, 3, true, ::iAnd),
+ /* 26 */ Instruction("rol", AddrMode.Zp, 5, true, ::iRol),
+ /* 27 */ Instruction("rla", AddrMode.Zp, 5, false, ::iRla),
+ /* 28 */ Instruction("plp", AddrMode.Imp, 4, true, ::iPlp),
+ /* 29 */ Instruction("and", AddrMode.Imm, 2, true, ::iAnd),
+ /* 2a */ Instruction("rol", AddrMode.Acc, 2, true, ::iRol),
+ /* 2b */ Instruction("anc", AddrMode.Imm, 2, false, ::iAnc),
+ /* 2c */ Instruction("bit", AddrMode.Abs, 4, true, ::iBit),
+ /* 2d */ Instruction("and", AddrMode.Abs, 4, true, ::iAnd),
+ /* 2e */ Instruction("rol", AddrMode.Abs, 6, true, ::iRol),
+ /* 2f */ Instruction("rla", AddrMode.Abs, 6, false, ::iRla),
+ /* 30 */ Instruction("bmi", AddrMode.Rel, 2, true, ::iBmi),
+ /* 31 */ Instruction("and", AddrMode.IzY, 5, true, ::iAnd),
+ /* 32 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 33 */ Instruction("rla", AddrMode.IzY, 8, false, ::iRla),
+ /* 34 */ Instruction("nop", AddrMode.ZpX, 4, false, ::iNop),
+ /* 35 */ Instruction("and", AddrMode.ZpX, 4, true, ::iAnd),
+ /* 36 */ Instruction("rol", AddrMode.ZpX, 6, true, ::iRol),
+ /* 37 */ Instruction("rla", AddrMode.ZpX, 6, false, ::iRla),
+ /* 38 */ Instruction("sec", AddrMode.Imp, 2, true, ::iSec),
+ /* 39 */ Instruction("and", AddrMode.AbsY, 4, true, ::iAnd),
+ /* 3a */ Instruction("nop", AddrMode.Imp, 2, false, ::iNop),
+ /* 3b */ Instruction("rla", AddrMode.AbsY, 7, false, ::iRla),
+ /* 3c */ Instruction("nop", AddrMode.AbsX, 4, false, ::iNop),
+ /* 3d */ Instruction("and", AddrMode.AbsX, 4, true, ::iAnd),
+ /* 3e */ Instruction("rol", AddrMode.AbsX, 7, true, ::iRol),
+ /* 3f */ Instruction("rla", AddrMode.AbsX, 7, false, ::iRla),
+ /* 40 */ Instruction("rti", AddrMode.Imp, 6, true, ::iRti),
+ /* 41 */ Instruction("eor", AddrMode.IzX, 6, true, ::iEor),
+ /* 42 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 43 */ Instruction("sre", AddrMode.IzX, 8, false, ::iSre),
+ /* 44 */ Instruction("nop", AddrMode.Zp, 3, false, ::iNop),
+ /* 45 */ Instruction("eor", AddrMode.Zp, 3, true, ::iEor),
+ /* 46 */ Instruction("lsr", AddrMode.Zp, 5, true, ::iLsr),
+ /* 47 */ Instruction("sre", AddrMode.Zp, 5, false, ::iSre),
+ /* 48 */ Instruction("pha", AddrMode.Imp, 3, true, ::iPha),
+ /* 49 */ Instruction("eor", AddrMode.Imm, 2, true, ::iEor),
+ /* 4a */ Instruction("lsr", AddrMode.Acc, 2, true, ::iLsr),
+ /* 4b */ Instruction("alr", AddrMode.Imm, 2, false, ::iAlr),
+ /* 4c */ Instruction("jmp", AddrMode.Abs, 3, true, ::iJmp),
+ /* 4d */ Instruction("eor", AddrMode.Abs, 4, true, ::iEor),
+ /* 4e */ Instruction("lsr", AddrMode.Abs, 6, true, ::iLsr),
+ /* 4f */ Instruction("sre", AddrMode.Abs, 6, false, ::iSre),
+ /* 50 */ Instruction("bvc", AddrMode.Rel, 2, true, ::iBvc),
+ /* 51 */ Instruction("eor", AddrMode.IzY, 5, true, ::iEor),
+ /* 52 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 53 */ Instruction("sre", AddrMode.IzY, 8, false, ::iSre),
+ /* 54 */ Instruction("nop", AddrMode.ZpX, 4, false, ::iNop),
+ /* 55 */ Instruction("eor", AddrMode.ZpX, 4, true, ::iEor),
+ /* 56 */ Instruction("lsr", AddrMode.ZpX, 6, true, ::iLsr),
+ /* 57 */ Instruction("sre", AddrMode.ZpX, 6, false, ::iSre),
+ /* 58 */ Instruction("cli", AddrMode.Imp, 2, true, ::iCli),
+ /* 59 */ Instruction("eor", AddrMode.AbsY, 4, true, ::iEor),
+ /* 5a */ Instruction("nop", AddrMode.Imp, 2, false, ::iNop),
+ /* 5b */ Instruction("sre", AddrMode.AbsY, 7, false, ::iSre),
+ /* 5c */ Instruction("nop", AddrMode.AbsX, 4, false, ::iNop),
+ /* 5d */ Instruction("eor", AddrMode.AbsX, 4, true, ::iEor),
+ /* 5e */ Instruction("lsr", AddrMode.AbsX, 7, true, ::iLsr),
+ /* 5f */ Instruction("sre", AddrMode.AbsX, 7, false, ::iSre),
+ /* 60 */ Instruction("rts", AddrMode.Imp, 6, true, ::iRts),
+ /* 61 */ Instruction("adc", AddrMode.IzX, 6, true, ::iAdc),
+ /* 62 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 63 */ Instruction("rra", AddrMode.IzX, 8, false, ::iRra),
+ /* 64 */ Instruction("nop", AddrMode.Zp, 3, false, ::iNop),
+ /* 65 */ Instruction("adc", AddrMode.Zp, 3, true, ::iAdc),
+ /* 66 */ Instruction("ror", AddrMode.Zp, 5, true, ::iRor),
+ /* 67 */ Instruction("rra", AddrMode.Zp, 5, false, ::iRra),
+ /* 68 */ Instruction("pla", AddrMode.Imp, 4, true, ::iPla),
+ /* 69 */ Instruction("adc", AddrMode.Imm, 2, true, ::iAdc),
+ /* 6a */ Instruction("ror", AddrMode.Acc, 2, true, ::iRor),
+ /* 6b */ Instruction("arr", AddrMode.Imm, 2, false, ::iArr),
+ /* 6c */ Instruction("jmp", AddrMode.Ind, 5, true, ::iJmp),
+ /* 6d */ Instruction("adc", AddrMode.Abs, 4, true, ::iAdc),
+ /* 6e */ Instruction("ror", AddrMode.Abs, 6, true, ::iRor),
+ /* 6f */ Instruction("rra", AddrMode.Abs, 6, false, ::iRra),
+ /* 70 */ Instruction("bvs", AddrMode.Rel, 2, true, ::iBvs),
+ /* 71 */ Instruction("adc", AddrMode.IzY, 5, true, ::iAdc),
+ /* 72 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 73 */ Instruction("rra", AddrMode.IzY, 8, false, ::iRra),
+ /* 74 */ Instruction("nop", AddrMode.ZpX, 4, false, ::iNop),
+ /* 75 */ Instruction("adc", AddrMode.ZpX, 4, true, ::iAdc),
+ /* 76 */ Instruction("ror", AddrMode.ZpX, 6, true, ::iRor),
+ /* 77 */ Instruction("rra", AddrMode.ZpX, 6, false, ::iRra),
+ /* 78 */ Instruction("sei", AddrMode.Imp, 2, true, ::iSei),
+ /* 79 */ Instruction("adc", AddrMode.AbsY, 4, true, ::iAdc),
+ /* 7a */ Instruction("nop", AddrMode.Imp, 2, false, ::iNop),
+ /* 7b */ Instruction("rra", AddrMode.AbsY, 7, false, ::iRra),
+ /* 7c */ Instruction("nop", AddrMode.AbsX, 4, false, ::iNop),
+ /* 7d */ Instruction("adc", AddrMode.AbsX, 4, true, ::iAdc),
+ /* 7e */ Instruction("ror", AddrMode.AbsX, 7, true, ::iRor),
+ /* 7f */ Instruction("rra", AddrMode.AbsX, 7, false, ::iRra),
+ /* 80 */ Instruction("nop", AddrMode.Imm, 2, false, ::iNop),
+ /* 81 */ Instruction("sta", AddrMode.IzX, 6, true, ::iSta),
+ /* 82 */ Instruction("nop", AddrMode.Imm, 2, false, ::iNop),
+ /* 83 */ Instruction("sax", AddrMode.IzX, 6, false, ::iSax),
+ /* 84 */ Instruction("sty", AddrMode.Zp, 3, true, ::iSty),
+ /* 85 */ Instruction("sta", AddrMode.Zp, 3, true, ::iSta),
+ /* 86 */ Instruction("stx", AddrMode.Zp, 3, true, ::iStx),
+ /* 87 */ Instruction("sax", AddrMode.Zp, 3, false, ::iSax),
+ /* 88 */ Instruction("dey", AddrMode.Imp, 2, true, ::iDey),
+ /* 89 */ Instruction("nop", AddrMode.Imm, 2, false, ::iNop),
+ /* 8a */ Instruction("txa", AddrMode.Imp, 2, true, ::iTxa),
+ /* 8b */ Instruction("xaa", AddrMode.Imm, 2, false, ::iXaa),
+ /* 8c */ Instruction("sty", AddrMode.Abs, 4, true, ::iSty),
+ /* 8d */ Instruction("sta", AddrMode.Abs, 4, true, ::iSta),
+ /* 8e */ Instruction("stx", AddrMode.Abs, 4, true, ::iStx),
+ /* 8f */ Instruction("sax", AddrMode.Abs, 4, true, ::iSax),
+ /* 90 */ Instruction("bcc", AddrMode.Rel, 2, true, ::iBcc),
+ /* 91 */ Instruction("sta", AddrMode.IzY, 6, true, ::iSta),
+ /* 92 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* 93 */ Instruction("ahx", AddrMode.IzY, 6, false, ::iAhx),
+ /* 94 */ Instruction("sty", AddrMode.ZpX, 4, true, ::iSty),
+ /* 95 */ Instruction("sta", AddrMode.ZpX, 4, true, ::iSta),
+ /* 96 */ Instruction("stx", AddrMode.ZpY, 4, true, ::iStx),
+ /* 97 */ Instruction("sax", AddrMode.ZpY, 4, false, ::iSax),
+ /* 98 */ Instruction("tya", AddrMode.Imp, 2, true, ::iTya),
+ /* 99 */ Instruction("sta", AddrMode.AbsY, 5, true, ::iSta),
+ /* 9a */ Instruction("txs", AddrMode.Imp, 2, true, ::iTxs),
+ /* 9b */ Instruction("tas", AddrMode.AbsY, 5, false, ::iTas),
+ /* 9c */ Instruction("shy", AddrMode.AbsX, 5, false, ::iShy),
+ /* 9d */ Instruction("sta", AddrMode.AbsX, 5, true, ::iSta),
+ /* 9e */ Instruction("shx", AddrMode.AbsY, 5, false, ::iShx),
+ /* 9f */ Instruction("ahx", AddrMode.AbsY, 5, false, ::iAhx),
+ /* a0 */ Instruction("ldy", AddrMode.Imm, 2, true, ::iLdy),
+ /* a1 */ Instruction("lda", AddrMode.IzX, 6, true, ::iLda),
+ /* a2 */ Instruction("ldx", AddrMode.Imm, 2, true, ::iLdx),
+ /* a3 */ Instruction("lax", AddrMode.IzX, 6, false, ::iLax),
+ /* a4 */ Instruction("ldy", AddrMode.Zp, 3, true, ::iLdy),
+ /* a5 */ Instruction("lda", AddrMode.Zp, 3, true, ::iLda),
+ /* a6 */ Instruction("ldx", AddrMode.Zp, 3, true, ::iLdx),
+ /* a7 */ Instruction("lax", AddrMode.Zp, 3, false, ::iLax),
+ /* a8 */ Instruction("tay", AddrMode.Imp, 2, true, ::iTay),
+ /* a9 */ Instruction("lda", AddrMode.Imm, 2, true, ::iLda),
+ /* aa */ Instruction("tax", AddrMode.Imp, 2, true, ::iTax),
+ /* ab */ Instruction("lax", AddrMode.Imm, 2, false, ::iLax),
+ /* ac */ Instruction("ldy", AddrMode.Abs, 4, true, ::iLdy),
+ /* ad */ Instruction("lda", AddrMode.Abs, 4, true, ::iLda),
+ /* ae */ Instruction("ldx", AddrMode.Abs, 4, true, ::iLdx),
+ /* af */ Instruction("lax", AddrMode.Abs, 4, false, ::iLax),
+ /* b0 */ Instruction("bcs", AddrMode.Rel, 2, true, ::iBcs),
+ /* b1 */ Instruction("lda", AddrMode.IzY, 5, true, ::iLda),
+ /* b2 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* b3 */ Instruction("lax", AddrMode.IzY, 5, false, ::iLax),
+ /* b4 */ Instruction("ldy", AddrMode.ZpX, 4, true, ::iLdy),
+ /* b5 */ Instruction("lda", AddrMode.ZpX, 4, true, ::iLda),
+ /* b6 */ Instruction("ldx", AddrMode.ZpY, 4, true, ::iLdx),
+ /* b7 */ Instruction("lax", AddrMode.ZpY, 4, false, ::iLax),
+ /* b8 */ Instruction("clv", AddrMode.Imp, 2, true, ::iClv),
+ /* b9 */ Instruction("lda", AddrMode.AbsY, 4, true, ::iLda),
+ /* ba */ Instruction("tsx", AddrMode.Imp, 2, true, ::iTsx),
+ /* bb */ Instruction("las", AddrMode.AbsY, 4, false, ::iLas),
+ /* bc */ Instruction("ldy", AddrMode.AbsX, 4, true, ::iLdy),
+ /* bd */ Instruction("lda", AddrMode.AbsX, 4, true, ::iLda),
+ /* be */ Instruction("ldx", AddrMode.AbsY, 4, true, ::iLdx),
+ /* bf */ Instruction("lax", AddrMode.AbsY, 4, false, ::iLax),
+ /* c0 */ Instruction("cpy", AddrMode.Imm, 2, true, ::iCpy),
+ /* c1 */ Instruction("cmp", AddrMode.IzX, 6, true, ::iCmp),
+ /* c2 */ Instruction("nop", AddrMode.Imm, 2, false, ::iNop),
+ /* c3 */ Instruction("dcp", AddrMode.IzX, 8, false, ::iDcp),
+ /* c4 */ Instruction("cpy", AddrMode.Zp, 3, true, ::iCpy),
+ /* c5 */ Instruction("cmp", AddrMode.Zp, 3, true, ::iCmp),
+ /* c6 */ Instruction("dec", AddrMode.Zp, 5, true, ::iDec),
+ /* c7 */ Instruction("dcp", AddrMode.Zp, 5, false, ::iDcp),
+ /* c8 */ Instruction("iny", AddrMode.Imp, 2, true, ::iIny),
+ /* c9 */ Instruction("cmp", AddrMode.Imm, 2, true, ::iCmp),
+ /* ca */ Instruction("dex", AddrMode.Imp, 2, true, ::iDex),
+ /* cb */ Instruction("axs", AddrMode.Imm, 2, false, ::iAxs),
+ /* cc */ Instruction("cpy", AddrMode.Abs, 4, true, ::iCpy),
+ /* cd */ Instruction("cmp", AddrMode.Abs, 4, true, ::iCmp),
+ /* ce */ Instruction("dec", AddrMode.Abs, 6, true, ::iDec),
+ /* cf */ Instruction("dcp", AddrMode.Abs, 6, false, ::iDcp),
+ /* d0 */ Instruction("bne", AddrMode.Rel, 2, true, ::iBne),
+ /* d1 */ Instruction("cmp", AddrMode.IzY, 5, true, ::iCmp),
+ /* d2 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* d3 */ Instruction("dcp", AddrMode.IzY, 8, false, ::iDcp),
+ /* d4 */ Instruction("nop", AddrMode.ZpX, 4, false, ::iNop),
+ /* d5 */ Instruction("cmp", AddrMode.ZpX, 4, true, ::iCmp),
+ /* d6 */ Instruction("dec", AddrMode.ZpX, 6, true, ::iDec),
+ /* d7 */ Instruction("dcp", AddrMode.ZpX, 6, false, ::iDcp),
+ /* d8 */ Instruction("cld", AddrMode.Imp, 2, true, ::iCld),
+ /* d9 */ Instruction("cmp", AddrMode.AbsY, 4, true, ::iCmp),
+ /* da */ Instruction("nop", AddrMode.Imp, 2, false, ::iNop),
+ /* db */ Instruction("dcp", AddrMode.AbsY, 7, false, ::iDcp),
+ /* dc */ Instruction("nop", AddrMode.AbsX, 4, false, ::iNop),
+ /* dd */ Instruction("cmp", AddrMode.AbsX, 4, true, ::iCmp),
+ /* de */ Instruction("dec", AddrMode.AbsX, 7, true, ::iDec),
+ /* df */ Instruction("dcp", AddrMode.AbsX, 7, false, ::iDcp),
+ /* e0 */ Instruction("cpx", AddrMode.Imm, 2, true, ::iCpx),
+ /* e1 */ Instruction("sbc", AddrMode.IzX, 6, true, ::iSbc),
+ /* e2 */ Instruction("nop", AddrMode.Imm, 2, false, ::iNop),
+ /* e3 */ Instruction("isc", AddrMode.IzX, 8, false, ::iIsc),
+ /* e4 */ Instruction("cpx", AddrMode.Zp, 3, true, ::iCpx),
+ /* e5 */ Instruction("sbc", AddrMode.Zp, 3, true, ::iSbc),
+ /* e6 */ Instruction("inc", AddrMode.Zp, 5, true, ::iInc),
+ /* e7 */ Instruction("isc", AddrMode.Zp, 5, false, ::iIsc),
+ /* e8 */ Instruction("inx", AddrMode.Imp, 2, true, ::iInx),
+ /* e9 */ Instruction("sbc", AddrMode.Imm, 2, true, ::iSbc),
+ /* ea */ Instruction("nop", AddrMode.Imp, 2, true, ::iNop),
+ /* eb */ Instruction("sbc", AddrMode.Imm, 2, false, ::iSbc),
+ /* ec */ Instruction("cpx", AddrMode.Abs, 4, true, ::iCpx),
+ /* ed */ Instruction("sbc", AddrMode.Abs, 4, true, ::iSbc),
+ /* ee */ Instruction("inc", AddrMode.Abs, 6, true, ::iInc),
+ /* ef */ Instruction("isc", AddrMode.Abs, 6, true, ::iIsc),
+ /* f0 */ Instruction("beq", AddrMode.Rel, 2, true, ::iBeq),
+ /* f1 */ Instruction("sbc", AddrMode.IzY, 5, true, ::iSbc),
+ /* f2 */ Instruction("???", AddrMode.Imp, 0, false, ::iInvalid),
+ /* f3 */ Instruction("isc", AddrMode.IzY, 8, false, ::iIsc),
+ /* f4 */ Instruction("nop", AddrMode.ZpX, 4, false, ::iNop),
+ /* f5 */ Instruction("sbc", AddrMode.ZpX, 4, true, ::iSbc),
+ /* f6 */ Instruction("inc", AddrMode.ZpX, 6, true, ::iInc),
+ /* f7 */ Instruction("isc", AddrMode.ZpX, 6, false, ::iIsc),
+ /* f8 */ Instruction("sed", AddrMode.Imp, 2, true, ::iSed),
+ /* f9 */ Instruction("sbc", AddrMode.AbsY, 4, true, ::iSbc),
+ /* fa */ Instruction("nop", AddrMode.Imp, 2, false, ::iNop),
+ /* fb */ Instruction("isc", AddrMode.AbsY, 7, false, ::iIsc),
+ /* fc */ Instruction("nop", AddrMode.AbsX, 4, false, ::iNop),
+ /* fd */ Instruction("sbc", AddrMode.AbsX, 4, true, ::iSbc),
+ /* fe */ Instruction("inc", AddrMode.AbsX, 7, true, ::iInc),
+ /* ff */ Instruction("isc", AddrMode.AbsX, 7, false, ::iIsc)
+ ).toTypedArray()
+
+
+ private fun iInvalid() {
+ throw InstructionError("invalid instruction encountered: opcode=${hexB(currentOpcode)} instr=${currentInstruction.mnemonic}")
+ }
+
+ // official instructions
+
+ private fun iAdc() {
+ val operand = if (currentInstruction.mode == AddrMode.Imm) {
+ fetchedData
+ } else {
+ read(fetchedAddress)
+ }
+ if (status.D) {
+ // BCD add
+ var lo = (A and 0x0f) + (operand and 0x0f) + if (status.C) 1 else 0
+ if (lo and 0xff > 9) lo += 6
+ var hi = (A shr 4) + (operand shr 4) + if (lo > 15) 1 else 0
+ if (hi and 0xff > 9) hi += 6
+ var result = lo and 0x0f or (hi shl 4)
+ result = result and 0xff
+ status.C = hi > 15
+ status.Z = result == 0
+ status.V = false // BCD never sets overflow flag
+ status.N = false // BCD is never negative on NMOS 6502 (bug)
+ } else {
+ // normal add
+ val result = A + operand + if (status.C) 1 else 0
+ status.C = result > 255
+ status.V = (A xor operand).inv() and (A xor result) and 0x0080 != 0
+ A = result and 255
+ status.N = (A and 0b10000000) != 0
+ status.Z = A == 0
+ }
+ }
+
+ private fun iAnd() {
+ A = A and fetchedData
+ status.Z = A == 0
+ status.N = (A and 0b10000000) != 0
+ }
+
+ private fun iAsl() {
+ if (currentInstruction.mode == AddrMode.Acc) {
+ status.C = (A and 0b10000000) == 1
+ A = (A shl 1) and 255
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ } else {
+ val data = read(fetchedAddress)
+ status.C = (data and 0b10000000) == 1
+ val shifted = (data shl 1) and 255
+ write(fetchedAddress, shifted)
+ status.Z = shifted == 0
+ status.N = (shifted and 0b10000000) == 1
+ }
+ }
+
+ private fun iBcc() {
+ if (!status.C) pc = fetchedAddress
+ }
+
+ private fun iBcs() {
+ if (status.C) pc = fetchedAddress
+ }
+
+ private fun iBeq() {
+ if (status.Z) pc = fetchedAddress
+ }
+
+ private fun iBit() {
+ status.Z = (A and fetchedData) == 0
+ status.V = (fetchedData and 0b01000000) != 0
+ status.N = (fetchedData and 0b10000000) != 0
+ }
+
+ private fun iBmi() {
+ if (status.N) pc = fetchedAddress
+ }
+
+ private fun iBne() {
+ if (!status.Z) pc = fetchedAddress
+ }
+
+ private fun iBpl() {
+ if (!status.N) pc = fetchedAddress
+ }
+
+ private fun iBrk() {
+ pc++
+ status.I = true
+ status.B = true
+ pushStackAddr(pc)
+ pushStack(status)
+ status.B = false
+ pc = readWord(IRQ_vector)
+ }
+
+ private fun iBvc() {
+ if (!status.V) pc = fetchedAddress
+ }
+
+ private fun iBvs() {
+ if (status.V) pc = fetchedAddress
+ }
+
+ private fun iClc() {
+ status.C = false
+ }
+
+ private fun iCld() {
+ status.D = false
+ }
+
+ private fun iCli() {
+ status.I = false
+ }
+
+ private fun iClv() {
+ status.V = false
+ }
+
+ private fun iCmp() {
+ val fetched =
+ if (currentInstruction.mode == AddrMode.Imm) {
+ fetchedData
+ } else {
+ read(fetchedAddress)
+ }
+ status.C = A >= fetched
+ status.Z = A == fetched
+ status.N = ((A - fetched) and 0b10000000) == 1
+ }
+
+ private fun iCpx() {
+ val fetched =
+ if (currentInstruction.mode == AddrMode.Imm) {
+ fetchedData
+ } else {
+ read(fetchedAddress)
+ }
+ status.C = X >= fetched
+ status.Z = X == fetched
+ status.N = ((X - fetched) and 0b10000000) == 1
+ }
+
+ private fun iCpy() {
+ val fetched =
+ if (currentInstruction.mode == AddrMode.Imm) {
+ fetchedData
+ } else {
+ read(fetchedAddress)
+ }
+ status.C = Y >= fetched
+ status.Z = Y == fetched
+ status.N = ((Y - fetched) and 0b10000000) == 1
+ }
+
+ private fun iDec() {
+ val data = (read(fetchedAddress) - 1) and 255
+ write(fetchedAddress, data)
+ status.Z = data == 0
+ status.N = (data and 0b10000000) == 1
+ }
+
+ private fun iDex() {
+ X = (X - 1) and 255
+ status.Z = X == 0
+ status.N = (X and 0b10000000) == 1
+ }
+
+ private fun iDey() {
+ Y = (Y - 1) and 255
+ status.Z = Y == 0
+ status.N = (Y and 0b10000000) == 1
+ }
+
+ private fun iEor() {
+ A = if (currentInstruction.mode == AddrMode.Imm) {
+ A xor fetchedData
+ } else {
+ A xor read(fetchedAddress)
+ }
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ }
+
+ private fun iInc() {
+ val data = (read(fetchedAddress) + 1) and 255
+ write(fetchedAddress, data)
+ status.Z = data == 0
+ status.N = (data and 0b10000000) == 1
+ }
+
+ private fun iInx() {
+ X = (X + 1) and 255
+ status.Z = X == 0
+ status.N = (X and 0b10000000) == 1
+ }
+
+ private fun iIny() {
+ Y = (Y + 1) and 255
+ status.Z = Y == 0
+ status.N = (Y and 0b10000000) == 1
+ }
+
+ private fun iJmp() {
+ pc = fetchedAddress
+ }
+
+ private fun iJsr() {
+ pushStackAddr(pc)
+ pc = fetchedAddress
+ }
+
+ private fun iLda() {
+ A = if (currentInstruction.mode == AddrMode.Imm)
+ fetchedData
+ else
+ read(fetchedAddress)
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ }
+
+ private fun iLdx() {
+ X = if (currentInstruction.mode == AddrMode.Imm)
+ fetchedData
+ else
+ read(fetchedAddress)
+ status.Z = X == 0
+ status.N = (X and 0b10000000) == 1
+ }
+
+ private fun iLdy() {
+ Y = if (currentInstruction.mode == AddrMode.Imm)
+ fetchedData
+ else
+ read(fetchedAddress)
+ status.Z = Y == 0
+ status.N = (Y and 0b10000000) == 1
+ }
+
+ private fun iLsr() {
+ if (currentInstruction.mode == AddrMode.Acc) {
+ status.C = (A and 1) == 1
+ A = A ushr 1
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ } else {
+ val data = read(fetchedAddress)
+ status.C = (data and 1) == 1
+ val shifted = data ushr 1
+ write(fetchedAddress, shifted)
+ status.Z = shifted == 0
+ status.N = (shifted and 0b10000000) == 1
+ }
+ }
+
+ private fun iNop() {}
+
+ private fun iOra() {
+ A = if (currentInstruction.mode == AddrMode.Imm)
+ A or fetchedData
+ else
+ A or read(fetchedAddress)
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ }
+
+ private fun iPha() {
+ pushStack(A)
+ }
+
+ private fun iPhp() {
+ pushStack(status)
+ }
+
+ private fun iPla() {
+ A = popStack()
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ }
+
+ private fun iPlp() {
+ status.fromByte(popStack())
+ }
+
+ private fun iRol() {
+ val oldCarry = status.C
+ if (currentInstruction.mode == AddrMode.Acc) {
+ status.C = (A and 0b10000000) == 1
+ A = (A shl 1) or (if (oldCarry) 1 else 0)
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ } else {
+ val data = read(fetchedAddress)
+ status.C = (data and 0b10000000) == 1
+ val shifted = (data shl 1) or (if (oldCarry) 1 else 0) and 255
+ write(fetchedAddress, shifted)
+ status.Z = shifted == 0
+ status.N = (shifted and 0b10000000) == 1
+ }
+ }
+
+ private fun iRor() {
+ val oldCarry = status.C
+ if (currentInstruction.mode == AddrMode.Acc) {
+ status.C = (A and 1) == 1
+ A = (A ushr 1) or (if (oldCarry) 0b10000000 else 0)
+ status.Z = A == 0
+ status.N = (A and 0b10000000) == 1
+ } else {
+ val data = read(fetchedAddress)
+ status.C = (data and 1) == 1
+ val shifted = (data ushr 1) or (if (oldCarry) 0b10000000 else 0)
+ write(fetchedAddress, shifted)
+ status.Z = shifted == 0
+ status.N = (shifted and 0b10000000) == 1
+ }
+ }
+
+ private fun iRti() {
+ status.fromByte(popStack())
+ pc = popStackAddr()
+ }
+
+ private fun iRts() {
+ pc = popStackAddr()
+ }
+
+ private fun iSbc() {
+ val operand = if (currentInstruction.mode == AddrMode.Imm) {
+ fetchedData
+ } else {
+ read(fetchedAddress)
+ }
+ if (status.D) {
+ var lo = (A and 0x0f) - (operand and 0x0f) - if (status.C) 0 else 1
+ if (lo and 0x10 != 0) lo -= 6
+ var h = (A shr 4) - (operand shr 4) - if (lo and 0x10 != 0) 1 else 0
+ if (h and 0x10 != 0) h -= 6
+ val result = lo and 0x0f or (h shl 4 and 0xff)
+ status.C = h and 255 < 15
+ status.Z = result == 0
+ status.V = false // BCD never sets overflow flag
+ status.N = false // BCD is never negative on NMOS 6502 (bug)
+ A = result and 255
+ } else {
+ // normal sub
+ val invertedOperand = operand xor 255
+ val result = A + invertedOperand + if (status.C) 1 else 0
+ status.C = result > 255
+ status.V = (A xor invertedOperand) and (A xor result) and 0x0080 != 0
+ A = result and 255
+ status.N = (A and 0b10000000) != 0
+ status.Z = A == 0
+ }
+ }
+
+ private fun iSec() {
+ status.C = true
+ }
+
+ private fun iSed() {
+ status.D = true
+ }
+
+ private fun iSei() {
+ status.I = true
+ }
+
+ private fun iSta() {
+ write(fetchedAddress, A)
+ }
+
+ private fun iStx() {
+ write(fetchedAddress, X)
+ }
+
+ private fun iSty() {
+ write(fetchedAddress, Y)
+ }
+
+ private fun iTax() {
+ X = A
+ status.Z = X == 0
+ status.N = (X and 0b10000000) != 0
+ }
+
+ private fun iTay() {
+ Y = A
+ status.Z = Y == 0
+ status.N = (Y and 0b10000000) != 0
+ }
+
+ private fun iTsx() {
+ X = status.asByte().toInt()
+ status.Z = X == 0
+ status.N = (X and 0b10000000) != 0
+ }
+
+ private fun iTxa() {
+ A = X
+ status.Z = A == 0
+ status.N = (A and 0b10000000) != 0
+ }
+
+ private fun iTxs() {
+ status.fromByte(X)
+ }
+
+ private fun iTya() {
+ A = Y
+ status.Z = A == 0
+ status.N = (A and 0b10000000) != 0
+ }
+
+ // unofficial/illegal instructions
+
+ private fun iAhx() {
+ TODO()
+ }
+
+ private fun iAlr() {
+ TODO()
+ }
+
+ private fun iAnc() {
+ TODO()
+ }
+
+ private fun iArr() {
+ TODO()
+ }
+
+ private fun iAxs() {
+ TODO()
+ }
+
+ private fun iDcp() {
+ TODO()
+ }
+
+ private fun iIsc() {
+ TODO()
+ }
+
+ private fun iLas() {
+ TODO()
+ }
+
+ private fun iLax() {
+ TODO()
+ }
+
+ private fun iRla() {
+ TODO()
+ }
+
+ private fun iRra() {
+ TODO()
+ }
+
+ private fun iSax() {
+ TODO()
+ }
+
+ private fun iShx() {
+ TODO()
+ }
+
+ private fun iShy() {
+ TODO()
+ }
+
+ private fun iSlo() {
+ TODO()
+ }
+
+ private fun iSre() {
+ TODO()
+ }
+
+ private fun iTas() {
+ TODO()
+ }
+
+ private fun iXaa() {
+ TODO()
+ }
+
+}
diff --git a/sim65/src/components/Parallel.kt b/sim65/src/components/Parallel.kt
new file mode 100644
index 000000000..f2f5d5b7f
--- /dev/null
+++ b/sim65/src/components/Parallel.kt
@@ -0,0 +1,39 @@
+package sim65.components
+
+import sim65.C64Screencodes
+
+/**
+ * A parallel output device (basically, prints bytes as characters to the screen)
+ * First address = data byte (8 parallel bits)
+ * Second address = control byte (bit 0 high = write byte)
+ */
+class Parallel(startAddress: Address, endAddress: Address) : MemMappedComponent(startAddress, endAddress) {
+ private var dataByte: UByte = 0
+
+ init {
+ require(endAddress - startAddress + 1 == 2) { "parallel needs exactly 2 memory bytes (data + control)" }
+ }
+
+ override fun clock() {}
+ override fun reset() {}
+
+ override fun read(address: Address): UByte {
+ return if (address == startAddress)
+ dataByte
+ else
+ 0
+ }
+
+ override fun write(address: Address, data: UByte) {
+ if (address == startAddress)
+ dataByte = data
+ else if (address == endAddress) {
+ if ((data.toInt() and 1) == 1) {
+ val char = C64Screencodes.decodeScreencode(listOf(dataByte), false).first()
+ println("PARALLEL WRITE: '$char'")
+ }
+ }
+ }
+
+ override fun cloneMem(): Array = listOf(dataByte, 0).toTypedArray()
+}
diff --git a/sim65/src/components/Ram.kt b/sim65/src/components/Ram.kt
new file mode 100644
index 000000000..a665428fb
--- /dev/null
+++ b/sim65/src/components/Ram.kt
@@ -0,0 +1,30 @@
+package sim65.components
+
+import java.io.File
+
+class Ram(startAddress: Address, endAddress: Address): MemMappedComponent(startAddress, endAddress) {
+ private val memory = ShortArray(endAddress-startAddress+1)
+
+ override fun read(address: Address): UByte = memory[address-startAddress]
+
+ override fun write(address: Address, data: UByte) {
+ memory[address-startAddress] = data
+ }
+
+ override fun cloneMem(): Array = memory.toTypedArray()
+
+ override fun clock() { }
+
+ override fun reset() { memory.fill(0) }
+
+ fun load(filename: String, address: Address) {
+ val bytes = File(filename).readBytes()
+ bytes.forEachIndexed { index, byte ->
+ memory[address+index] =
+ if(byte>=0)
+ byte.toShort()
+ else
+ (256+byte).toShort()
+ }
+ }
+}
diff --git a/sim65/src/components/Rom.kt b/sim65/src/components/Rom.kt
new file mode 100644
index 000000000..9eee82b94
--- /dev/null
+++ b/sim65/src/components/Rom.kt
@@ -0,0 +1,15 @@
+package sim65.components
+
+class Rom(startAddress: Address, endAddress: Address, data: Array): MemMappedComponent(startAddress, endAddress) {
+ private val memory = ShortArray(data.size) { index -> data[index] }
+
+ init {
+ require(endAddress-startAddress+1 == data.size) { "rom address range doesn't match size of data bytes" }
+ }
+
+ override fun read(address: Address): UByte = memory[address-startAddress]
+ override fun write(address: Address, data: UByte) { }
+ override fun cloneMem(): Array = memory.toTypedArray()
+ override fun clock() { }
+ override fun reset() { }
+}
diff --git a/sim65/src/components/Timer.kt b/sim65/src/components/Timer.kt
new file mode 100644
index 000000000..5cbf2a42f
--- /dev/null
+++ b/sim65/src/components/Timer.kt
@@ -0,0 +1,29 @@
+package sim65.components
+
+class Timer(startAddress: Address, endAddress: Address): MemMappedComponent(startAddress, endAddress) {
+ private var cycle: Long = 0
+
+ init {
+ require(endAddress - startAddress + 1 == 4) { "timer needs exactly 4 memory bytes" }
+ }
+
+ override fun clock() {
+ cycle++
+ if (cycle > 0xffffffff)
+ cycle = 0
+ }
+
+ override fun reset() {
+ cycle = 0
+ }
+
+ override fun read(address: Address): UByte {
+ TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+ }
+
+ override fun write(address: Address, data: UByte) {
+ TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+ }
+
+ override fun cloneMem(): Array = TODO("clonemem timer")
+}
diff --git a/sim65/testprogram.asm b/sim65/testprogram.asm
new file mode 100644
index 000000000..631e6ee12
--- /dev/null
+++ b/sim65/testprogram.asm
@@ -0,0 +1,29 @@
+
+ * = $8000
+
+ sec
+ lda #$a0
+ sta $2000
+ ldx $2000
+ txa
+ tay
+ iny
+ sty $2001
+ ldy #0
+loop lda text,y
+ beq end
+ sta $d000
+ inc $d001
+ iny
+ jmp loop
+end nop
+ bvs loop
+
+ .byte $02 ; invalid opcode
+ .byte $02 ; invalid opcode
+ .byte $02 ; invalid opcode
+
+
+text .enc "screen"
+ .text "hello!",0
+ .enc "none"