From 8d6542905d33e75a1c646a84c091fdbf86c56a99 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Sat, 31 Aug 2019 17:45:11 +0200 Subject: [PATCH] beginnings of 6502 cpu simulator --- .idea/modules.xml | 1 + compiler/src/prog8/CompilerMain.kt | 1 - examples/arithmetic/bcd.asm | 228 ++++++ settings.gradle | 3 +- sim65/build.gradle | 108 +++ sim65/res/version.txt | 1 + sim65/sim65.iml | 24 + sim65/src/C64Screencodes.kt | 550 ++++++++++++++ sim65/src/Sim65Main.kt | 88 +++ sim65/src/components/Bus.kt | 46 ++ sim65/src/components/Component.kt | 35 + sim65/src/components/Cpu6502.kt | 1138 ++++++++++++++++++++++++++++ sim65/src/components/Parallel.kt | 39 + sim65/src/components/Ram.kt | 30 + sim65/src/components/Rom.kt | 15 + sim65/src/components/Timer.kt | 29 + sim65/testprogram.asm | 29 + 17 files changed, 2363 insertions(+), 2 deletions(-) create mode 100644 examples/arithmetic/bcd.asm create mode 100644 sim65/build.gradle create mode 100644 sim65/res/version.txt create mode 100644 sim65/sim65.iml create mode 100644 sim65/src/C64Screencodes.kt create mode 100644 sim65/src/Sim65Main.kt create mode 100644 sim65/src/components/Bus.kt create mode 100644 sim65/src/components/Component.kt create mode 100644 sim65/src/components/Cpu6502.kt create mode 100644 sim65/src/components/Parallel.kt create mode 100644 sim65/src/components/Ram.kt create mode 100644 sim65/src/components/Rom.kt create mode 100644 sim65/src/components/Timer.kt create mode 100644 sim65/testprogram.asm 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"