beginnings of 6502 cpu simulator

This commit is contained in:
Irmen de Jong 2019-08-31 17:45:11 +02:00
parent 39798a1a4f
commit 8d6542905d
17 changed files with 2363 additions and 2 deletions

View File

@ -7,6 +7,7 @@
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
<module fileurl="file://$PROJECT_DIR$/sim65/sim65.iml" filepath="$PROJECT_DIR$/sim65/sim65.iml" />
</modules>
</component>
</project>

View File

@ -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

228
examples/arithmetic/bcd.asm Normal file
View File

@ -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
;--------------------------------------------------------------------------

View File

@ -1,2 +1,3 @@
include ':parser'
include ':compiler'
include ':compiler'
include ':sim65'

108
sim65/build.gradle Normal file
View File

@ -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"
}

1
sim65/res/version.txt Normal file
View File

@ -0,0 +1 @@
0.1

24
sim65/sim65.iml Normal file
View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="kotlin-language" name="Kotlin">
<configuration version="3" platform="JVM 1.8" allPlatforms="JVM [1.8]" useProjectSettings="false">
<compilerSettings />
<compilerArguments>
<option name="jvmTarget" value="1.8" />
</compilerArguments>
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/res" type="java-resource" />
</content>
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
<orderEntry type="library" name="kotlinx-cli-jvm-0.1.0-dev-5" level="project" />
</component>
</module>

550
sim65/src/C64Screencodes.kt Normal file
View File

@ -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<Short> {
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<Short>, lowercase: Boolean = false): String {
val decodeTable = if(lowercase) decodingScreencodeLowercase else decodingScreencodeUppercase
return screencode.map { decodeTable[it.toInt()] }.joinToString("")
}
}

88
sim65/src/Sim65Main.kt Normal file
View File

@ -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<String>) {
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<String>) {
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<UByte>(
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,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)
}

View File

@ -0,0 +1,46 @@
package sim65.components
typealias UByte = Short
typealias Address = Int
class Bus {
private val components = mutableListOf<BusComponent>()
private val memComponents = mutableListOf<MemMappedComponent>()
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)
}
}
}

View File

@ -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<UByte>
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()
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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<UByte> = listOf(dataByte, 0).toTypedArray()
}

View File

@ -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<UByte> = 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()
}
}
}

View File

@ -0,0 +1,15 @@
package sim65.components
class Rom(startAddress: Address, endAddress: Address, data: Array<UByte>): 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<UByte> = memory.toTypedArray()
override fun clock() { }
override fun reset() { }
}

View File

@ -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<UByte> = TODO("clonemem timer")
}

29
sim65/testprogram.asm Normal file
View File

@ -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"