1
0
mirror of https://github.com/irmen/ksim65.git synced 2024-06-16 10:29:30 +00:00

Compare commits

...

22 Commits
v1.8 ... master

Author SHA1 Message Date
Irmen de Jong
3f28ef6cad update gradle 2023-10-30 20:24:14 +01:00
Irmen de Jong
0fda62370e updates 2023-08-30 13:59:16 +02:00
Irmen de Jong
6812ab4b09 updates 2023-08-15 13:30:01 +02:00
Irmen de Jong
67c70abb41 build fixes 2023-05-26 20:14:55 +02:00
Irmen de Jong
5cc3f8fddd kotlin 1.7.10 2022-08-13 21:24:12 +02:00
Irmen de Jong
87c7aad49a update gradle wrapper to 7.4 2022-03-12 23:22:46 +01:00
Irmen de Jong
1ca771840b kotlin 1.6.10 2022-01-09 03:53:50 +01:00
Irmen de Jong
b17b72b6b5 remote broken dokka build target, upgrade to kotlin 1.5.30 2021-09-19 18:27:25 +02:00
Irmen de Jong
61bb920776 ide files 2021-09-19 18:12:35 +02:00
Irmen de Jong
6c73852b5e update kotlin version 2021-07-06 23:37:19 +02:00
Irmen de Jong
6dc433417d updated to Kotlin 1.4.20 and JDK 11 2020-12-19 14:58:48 +01:00
Irmen de Jong
f8b3213b41 update travix 2020-10-23 00:07:26 +02:00
Irmen de Jong
b2b4b0ab83 gradle upgrade 2020-10-22 23:53:59 +02:00
Irmen de Jong
1cbb006577 updated to new kotlin version 2020-10-13 00:06:28 +02:00
Irmen de Jong
b8691db38e version 1.9 2020-07-07 18:50:02 +02:00
Irmen de Jong
d1d433c3a6 replaced fixed 8x16 bitmap font by psf font 2020-03-17 00:01:45 +01:00
Irmen de Jong
3925c80258 another link to illegal instructions 2020-03-16 21:59:38 +01:00
Irmen de Jong
c2d6954327 added default vice rom path to search list 2020-03-13 17:16:41 +01:00
Irmen de Jong
0670a85f88 upgrade kotlin sdk version, optimized hex number output 2020-03-07 01:17:48 +01:00
Irmen de Jong
7522d3cb3b added joystick to C64 emulation (via numpad keys) 2020-03-01 18:38:00 +01:00
Irmen de Jong
99942748f9 when 2020-02-27 03:57:32 +01:00
Irmen de Jong
323098f645 doc 2020-02-21 20:11:17 +01:00
47 changed files with 698 additions and 398 deletions

6
.idea/compiler.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>

View File

@ -15,8 +15,6 @@
<option value="$PROJECT_DIR$" />
</set>
</option>
<option name="useAutoImport" value="true" />
<option name="useQualifiedModuleNames" value="true" />
</GradleProjectSettings>
</option>
</component>

View File

@ -26,5 +26,10 @@
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
<remote-repository>
<option name="id" value="MavenRepo" />
<option name="name" value="MavenRepo" />
<option name="url" value="https://repo.maven.apache.org/maven2/" />
</remote-repository>
</component>
</project>

6
.idea/kotlinc.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.10" />
</component>
</project>

View File

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4" />

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="FrameworkDetectionExcludesConfiguration">
<file type="web" url="file://$PROJECT_DIR$" />
</component>
<component name="JavaScriptSettings">
<option name="languageLevel" value="ES6" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="openjdk-11" project-jdk-type="JavaSDK" />
</project>

View File

@ -1,10 +0,0 @@
language: java
sudo: false
# jdk: openjdk8
# dist: xenial
before_install:
- chmod +x gradlew
script:
- gradle test

View File

@ -1,14 +1,7 @@
[![saythanks](https://img.shields.io/badge/say-thanks-ff69b4.svg)](https://saythanks.io/to/irmen)
[![Build Status](https://travis-ci.org/irmen/ksim65.svg?branch=master)](https://travis-ci.org/irmen/ksim65)
JCenter: [![Download from Jcenter](https://api.bintray.com/packages/irmen/maven/ksim65/images/download.svg)](https://bintray.com/irmen/maven/ksim65/_latestVersion)
# KSim65 - Kotlin/JVM 6502/65C02 microprocessor simulator
*Written by Irmen de Jong (irmen@razorvine.net)*
*Software license: MIT, see file LICENSE*
![6502](https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/KL_MOS_6502.jpg/320px-KL_MOS_6502.jpg)
This is a Kotlin/JVM library that simulates the 8-bit 6502 and 65C02 microprocessors,
@ -33,7 +26,7 @@ Still to be written. For now, use the source ;-)
## Using it as a library in your own project
Ksim65 is available on the [JCenter maven repository](https://bintray.com/irmen/maven/ksim65/_latestVersion).
**TODO move to another repository for published packages.**
You can simply add it as a dependency to your project.
For Maven:
@ -41,13 +34,13 @@ For Maven:
<dependency>
<groupId>net.razorvine</groupId>
<artifactId>ksim65</artifactId>
<version>1.8</version>
<version>1.10</version>
<type>pom</type>
</dependency>
For Gradle:
implementation 'net.razorvine:ksim65:1.8'
implementation 'net.razorvine:ksim65:1.10'
Update the version as required.
@ -67,3 +60,36 @@ various timers and IRQs. It's not cycle perfect, and the video display is drawn
so raster splits/rasterbars are impossible. But many other things work fine.
![C64 emulation](c64.png)
### License information
Ksim65 itself is licensed under the MIT software license, see file LICENSE.
It includes the 'Spleen' bitmap font (https://github.com/fcambus/spleen),
which has the following license (BSD):
Copyright (c) 2018-2020, Frederic Cambus
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,4 +1,3 @@
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import java.util.*
import kotlin.math.max
@ -6,32 +5,39 @@ import kotlin.math.max
plugins {
// Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
kotlin("jvm") version "1.3.61"
kotlin("jvm") version "1.9.10"
`maven-publish`
application
id("org.jetbrains.dokka") version "0.10.0"
id("com.jfrog.bintray") version "1.8.4"
java
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
allprojects {
val versionProps = Properties().also {
it.load(File("$projectDir/src/main/resources/version.properties").inputStream())
}
version = versionProps["version"] as String
group = "net.razorvine"
base.archivesBaseName = "ksim65"
// base.archivesBaseName = "ksim65"
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
mavenLocal()
jcenter()
mavenCentral()
maven("https://jitpack.io")
}
}
dependencies {
// Align versions of all Kotlin components
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-reflect")
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
@ -40,11 +46,11 @@ dependencies {
// Use the Kotlin JUnit5 integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.4.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.0")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
subprojects.forEach {
archives(it)
implementation(it)
}
}
@ -61,35 +67,27 @@ tasks {
}
withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
named<DokkaTask>("dokka") {
outputFormat = "html"
outputDirectory = "$buildDir/kdoc"
configuration {
skipEmptyPackages = true
}
kotlinOptions.jvmTarget = "11"
}
}
val c64emuScript by tasks.registering(CreateStartScripts::class) {
outputDir = File(project.buildDir, "bin")
outputDir = project.layout.buildDirectory.dir("bin").get().asFile
applicationName = "c64emu"
mainClassName = "razorvine.c64emu.C64MainKt"
mainClass.set("razorvine.c64emu.C64MainKt")
classpath = project.tasks["jar"].outputs.files+project.configurations.runtimeClasspath.get()
}
val ehbasicScript by tasks.registering(CreateStartScripts::class) {
outputDir = File(project.buildDir, "bin")
outputDir = project.layout.buildDirectory.dir("bin").get().asFile
applicationName = "ehbasic"
mainClassName = "razorvine.examplemachines.EhBasicMainKt"
mainClass.set("razorvine.examplemachines.EhBasicMainKt")
classpath = project.tasks["jar"].outputs.files+project.configurations.runtimeClasspath.get()
}
application {
applicationName = "ksim65vm"
mainClassName = "razorvine.examplemachines.MachineMainKt"
mainClass.set("razorvine.examplemachines.MachineMainKt")
applicationDistribution.into("bin") {
from(c64emuScript, ehbasicScript)
fileMode = 493
@ -102,12 +100,6 @@ val sourcesJar by tasks.registering(Jar::class) {
from(sourceSets.main.get().allSource)
}
val dokkaDocs by tasks.registering(Jar::class) {
dependsOn("dokka")
archiveClassifier.set("kdoc")
from(fileTree(File(project.buildDir, "kdoc")))
}
publishing {
repositories {
mavenLocal()
@ -116,28 +108,6 @@ publishing {
register("mavenJava", MavenPublication::class) {
from(components["java"])
artifact(sourcesJar.get())
artifact(dokkaDocs.get())
}
}
}
bintray {
user = System.getenv("BINTRAY_USER")
key = System.getenv("BINTRAY_KEY")
setPublications(* publishing.publications.names.toTypedArray())
// setConfigurations("archives")
pkg = PackageConfig().also {
it.name = "ksim65"
it.repo = "maven"
it.setLicenses("MIT")
it.vcsUrl = "https://github.com/irmen/ksim65.git"
it.setLabels("6502", "retro", "emulation", "c64")
it.githubRepo = it.vcsUrl
it.version = VersionConfig().also {
it.gpg = GpgConfig().also {
it.sign = true
}
}
}
}

BIN
c64testprgs/joytest.prg Normal file

Binary file not shown.

BIN
free-c64-roms/basic Normal file

Binary file not shown.

BIN
free-c64-roms/chargen Normal file

Binary file not shown.

BIN
free-c64-roms/kernal Normal file

Binary file not shown.

24
free-c64-roms/readme.txt Normal file
View File

@ -0,0 +1,24 @@
Free/open source roms for the C64
See https://github.com/MEGA65/open-roms
The following copyright notices apply to the entirety of this package,
including each source file, unless otherwise noted in each file or directory.
Copyright Paul Gardner-Stephen, 2019.
Copyright Roman Standzikowski (FeralChil64), 2019-2020.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

View File

@ -2,3 +2,4 @@ org.gradle.caching=true
org.gradle.console=rich
org.gradle.parallel=true
org.gradle.daemon=true
kotlin.code.style=official

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

269
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@ -17,78 +17,113 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@ -105,79 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

25
gradlew.bat vendored
View File

@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -51,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -61,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

View File

@ -9,12 +9,20 @@ import java.awt.event.KeyEvent
/**
* Minimal simulation of the MOS 6526 CIA chip.
* Depending on what CIA it is (1 or 2), some registers do different things on the C64.
* This implementation provides a working keyboard matrix, TOD clock, and the essentials of the timer A and B.
* This implementation provides a working keyboard matrix, joystick in port#2 (cia 1),
* time of day clock, and the essentials of the timer A and B.
*/
class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu: Cpu6502) : MemMappedComponent(startAddress, endAddress) {
private var ramBuffer = Array<UByte>(endAddress-startAddress+1) { 0 }
private var regPRA = 0xff
// joystick in port 2 configuration (only works on cia#1)
private var joy2up = false
private var joy2down = false
private var joy2left = false
private var joy2right = false
private var joy2fire = false
class TimeOfDay {
private var updatedAt = 0L
private var startedAt = 0L
@ -125,6 +133,11 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
timerAset = 0
timerBactual = 0
timerBset = 0
joy2up = false
joy2down = false
joy2left = false
joy2right = false
joy2fire = false
}
override operator fun get(offset: Int): UByte {
@ -139,71 +152,85 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
val register = offset and 15
if (number == 1 && register == 0x01) {
// register 1 on CIA#1 is the keyboard data port
// if bit is cleared in PRA, contains keys pressed in that column of the matrix
return when (regPRA) {
0b00000000 -> {
// check if any keys are pressed at all (by checking all columns at once)
if (hostKeyPresses.isEmpty()) 0xff.toShort() else 0x00.toShort()
}
0b11111110 -> {
// read column 0
scanColumn(HostKeyPress(KeyEvent.VK_DOWN), HostKeyPress(KeyEvent.VK_F5), HostKeyPress(KeyEvent.VK_F3),
HostKeyPress(KeyEvent.VK_F1), HostKeyPress(KeyEvent.VK_F7), HostKeyPress(KeyEvent.VK_RIGHT),
HostKeyPress(KeyEvent.VK_ENTER), HostKeyPress(KeyEvent.VK_BACK_SPACE))
}
0b11111101 -> {
// read column 1
scanColumn(HostKeyPress(KeyEvent.VK_SHIFT), // left shift
HostKeyPress(KeyEvent.VK_E), HostKeyPress(KeyEvent.VK_S), HostKeyPress(KeyEvent.VK_Z),
HostKeyPress(KeyEvent.VK_4), HostKeyPress(KeyEvent.VK_A), HostKeyPress(KeyEvent.VK_W),
HostKeyPress(KeyEvent.VK_3))
}
0b11111011 -> {
// read column 2
scanColumn(HostKeyPress(KeyEvent.VK_X), HostKeyPress(KeyEvent.VK_T), HostKeyPress(KeyEvent.VK_F),
HostKeyPress(KeyEvent.VK_C), HostKeyPress(KeyEvent.VK_6), HostKeyPress(KeyEvent.VK_D),
HostKeyPress(KeyEvent.VK_R), HostKeyPress(KeyEvent.VK_5))
}
0b11110111 -> {
// read column 3
scanColumn(HostKeyPress(KeyEvent.VK_V), HostKeyPress(KeyEvent.VK_U), HostKeyPress(KeyEvent.VK_H),
HostKeyPress(KeyEvent.VK_B), HostKeyPress(KeyEvent.VK_8), HostKeyPress(KeyEvent.VK_G),
HostKeyPress(KeyEvent.VK_Y), HostKeyPress(KeyEvent.VK_7))
}
0b11101111 -> {
// read column 4
scanColumn(HostKeyPress(KeyEvent.VK_N), HostKeyPress(KeyEvent.VK_O), HostKeyPress(KeyEvent.VK_K),
HostKeyPress(KeyEvent.VK_M), HostKeyPress(KeyEvent.VK_0), HostKeyPress(KeyEvent.VK_J),
HostKeyPress(KeyEvent.VK_I), HostKeyPress(KeyEvent.VK_9))
}
0b11011111 -> {
// read column 5
scanColumn(HostKeyPress(KeyEvent.VK_COMMA), HostKeyPress(KeyEvent.VK_AT), HostKeyPress(KeyEvent.VK_COLON),
HostKeyPress(KeyEvent.VK_PERIOD), HostKeyPress(KeyEvent.VK_MINUS), HostKeyPress(KeyEvent.VK_L),
HostKeyPress(KeyEvent.VK_P), HostKeyPress(KeyEvent.VK_PLUS))
}
0b10111111 -> {
// read column 6
scanColumn(HostKeyPress(KeyEvent.VK_SLASH), HostKeyPress(KeyEvent.VK_CIRCUMFLEX), HostKeyPress(KeyEvent.VK_EQUALS),
HostKeyPress(KeyEvent.VK_SHIFT, rightSide = true), // right shift
HostKeyPress(KeyEvent.VK_HOME), HostKeyPress(KeyEvent.VK_SEMICOLON), HostKeyPress(KeyEvent.VK_ASTERISK),
HostKeyPress(KeyEvent.VK_DEAD_TILDE) // pound sign
)
}
0b01111111 -> {
// read column 7
scanColumn(HostKeyPress(KeyEvent.VK_ESCAPE), HostKeyPress(KeyEvent.VK_Q), HostKeyPress(KeyEvent.VK_ALT),
HostKeyPress(KeyEvent.VK_SPACE), HostKeyPress(KeyEvent.VK_2), HostKeyPress(KeyEvent.VK_CONTROL),
HostKeyPress(KeyEvent.VK_BACK_QUOTE), HostKeyPress(KeyEvent.VK_1))
}
else -> {
// invalid column selection
0xff
if(number==1) {
// first CIA has keyboard matrix
if(register==0x00) {
// reading $dc00 is joystick in port #2
return (0b01100000
or (if(joy2up) 0 else 0b00000001)
or (if(joy2down) 0 else 0b00000010)
or (if(joy2left) 0 else 0b00000100)
or (if(joy2right) 0 else 0b00001000)
or (if(joy2fire) 0 else 0b00010000)).toShort()
} else if(register==0x01) {
// register 1 on CIA#1 is the keyboard data port (and joystick #1...)
// if bit is cleared in PRA, contains keys pressed in that column of the matrix
// NOTE: we do not emulate a joystick in port #1 as this conflicts with the keyboard.
// just use the joystick in port #2 for now...
return when (regPRA) {
0b00000000 -> {
// check if any keys are pressed at all (by checking all columns at once)
if (hostKeyPresses.isEmpty()) 0xff.toShort() else 0x00.toShort()
}
0b11111110 -> {
// read column 0
scanColumn(HostKeyPress(KeyEvent.VK_DOWN), HostKeyPress(KeyEvent.VK_F5), HostKeyPress(KeyEvent.VK_F3),
HostKeyPress(KeyEvent.VK_F1), HostKeyPress(KeyEvent.VK_F7), HostKeyPress(KeyEvent.VK_RIGHT),
HostKeyPress(KeyEvent.VK_ENTER), HostKeyPress(KeyEvent.VK_BACK_SPACE))
}
0b11111101 -> {
// read column 1
scanColumn(HostKeyPress(KeyEvent.VK_SHIFT), // left shift
HostKeyPress(KeyEvent.VK_E), HostKeyPress(KeyEvent.VK_S), HostKeyPress(KeyEvent.VK_Z),
HostKeyPress(KeyEvent.VK_4), HostKeyPress(KeyEvent.VK_A), HostKeyPress(KeyEvent.VK_W),
HostKeyPress(KeyEvent.VK_3))
}
0b11111011 -> {
// read column 2
scanColumn(HostKeyPress(KeyEvent.VK_X), HostKeyPress(KeyEvent.VK_T), HostKeyPress(KeyEvent.VK_F),
HostKeyPress(KeyEvent.VK_C), HostKeyPress(KeyEvent.VK_6), HostKeyPress(KeyEvent.VK_D),
HostKeyPress(KeyEvent.VK_R), HostKeyPress(KeyEvent.VK_5))
}
0b11110111 -> {
// read column 3
scanColumn(HostKeyPress(KeyEvent.VK_V), HostKeyPress(KeyEvent.VK_U), HostKeyPress(KeyEvent.VK_H),
HostKeyPress(KeyEvent.VK_B), HostKeyPress(KeyEvent.VK_8), HostKeyPress(KeyEvent.VK_G),
HostKeyPress(KeyEvent.VK_Y), HostKeyPress(KeyEvent.VK_7))
}
0b11101111 -> {
// read column 4
scanColumn(HostKeyPress(KeyEvent.VK_N), HostKeyPress(KeyEvent.VK_O), HostKeyPress(KeyEvent.VK_K),
HostKeyPress(KeyEvent.VK_M), HostKeyPress(KeyEvent.VK_0), HostKeyPress(KeyEvent.VK_J),
HostKeyPress(KeyEvent.VK_I), HostKeyPress(KeyEvent.VK_9))
}
0b11011111 -> {
// read column 5
scanColumn(HostKeyPress(KeyEvent.VK_COMMA), HostKeyPress(KeyEvent.VK_AT), HostKeyPress(KeyEvent.VK_COLON),
HostKeyPress(KeyEvent.VK_PERIOD), HostKeyPress(KeyEvent.VK_MINUS), HostKeyPress(KeyEvent.VK_L),
HostKeyPress(KeyEvent.VK_P), HostKeyPress(KeyEvent.VK_PLUS))
}
0b10111111 -> {
// read column 6
scanColumn(HostKeyPress(KeyEvent.VK_SLASH), HostKeyPress(KeyEvent.VK_CIRCUMFLEX), HostKeyPress(KeyEvent.VK_EQUALS),
HostKeyPress(KeyEvent.VK_SHIFT, rightSide = true), // right shift
HostKeyPress(KeyEvent.VK_HOME), HostKeyPress(KeyEvent.VK_SEMICOLON), HostKeyPress(KeyEvent.VK_ASTERISK),
HostKeyPress(KeyEvent.VK_DEAD_TILDE) // pound sign
)
}
0b01111111 -> {
// read column 7
scanColumn(HostKeyPress(KeyEvent.VK_ESCAPE), HostKeyPress(KeyEvent.VK_Q), HostKeyPress(KeyEvent.VK_ALT),
HostKeyPress(KeyEvent.VK_SPACE), HostKeyPress(KeyEvent.VK_2), HostKeyPress(KeyEvent.VK_CONTROL),
HostKeyPress(KeyEvent.VK_BACK_QUOTE), HostKeyPress(KeyEvent.VK_1))
}
else -> {
// invalid column selection
0xff
}
}
}
} else ramBuffer[register]
}
return when (register) {
0x04 -> (timerAactual and 0xff).toShort()
@ -224,19 +251,6 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
}
private fun toBCD(data: Int): UByte {
val tens = data/10
val ones = data-tens*10
return ((tens shl 4) or ones).toShort()
}
private fun fromBCD(bcd: UByte): Int {
val ibcd = bcd.toInt()
val tens = ibcd ushr 4
val ones = ibcd and 0x0f
return tens*10+ones
}
override operator fun set(offset: Int, data: UByte) {
val register = offset and 15
if (number == 1 && register == 0x00) {
@ -296,6 +310,19 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
}
private fun toBCD(data: Int): UByte {
val tens = data/10
val ones = data-tens*10
return ((tens shl 4) or ones).toShort()
}
private fun fromBCD(bcd: UByte): Int {
val ibcd = bcd.toInt()
val tens = ibcd ushr 4
val ones = ibcd and 0x0f
return tens*10+ones
}
fun hostKeyPressed(event: KeyEvent) {
val rightSide = event.keyLocation == KeyEvent.KEY_LOCATION_RIGHT
val numpad = event.keyLocation == KeyEvent.KEY_LOCATION_NUMPAD
@ -374,4 +401,15 @@ class Cia(val number: Int, startAddress: Address, endAddress: Address, val cpu:
}
}
}
fun setJoystick2(up: Boolean, down: Boolean, left: Boolean, right: Boolean, fire: Boolean) {
if(number!=1)
throw NotImplementedError("joystick port 2 is connected to cia#1")
joy2up = up
joy2down = down
joy2left = left
joy2right = right
joy2fire = fire
}
}

View File

@ -82,16 +82,50 @@ class MainC64Window(title: String, chargen: Rom, val ram: MemoryComponent, val c
// keyboard events:
override fun keyTyped(event: KeyEvent) {}
private var joy2up = false
private var joy2down = false
private var joy2left = false
private var joy2right = false
private var joy2fire = false
override fun keyPressed(event: KeyEvent) {
// '\' is mapped as RESTORE, this causes a NMI on the cpu
if (event.keyChar == '\\') {
cpu.nmiAsserted = true
} else {
keypressCia.hostKeyPressed(event)
if(event.keyLocation==KeyEvent.KEY_LOCATION_NUMPAD) {
// numpad is joystick #2
if (event.keyChar in "789") joy2up = true
if (event.keyChar in "123") joy2down = true
if (event.keyChar in "741") joy2left = true
if (event.keyChar in "963") joy2right = true
if (event.keyChar in "05\n") joy2fire = true
keypressCia.setJoystick2(joy2up, joy2down, joy2left, joy2right, joy2fire)
} else {
keypressCia.hostKeyPressed(event)
}
}
}
override fun keyReleased(event: KeyEvent) {
keypressCia.hostKeyPressed(event)
if(event.keyLocation==KeyEvent.KEY_LOCATION_NUMPAD) {
// numpad is joystick #2
if (event.keyChar in "789") joy2up = false
if (event.keyChar in "123") joy2down = false
if (event.keyChar in "741") joy2left = false
if (event.keyChar in "963") joy2right = false
if (event.keyChar in "05\n") joy2fire = false
keypressCia.setJoystick2(joy2up, joy2down, joy2left, joy2right, joy2fire)
} else {
keypressCia.hostKeyPressed(event)
}
}
fun reset() {
joy2up = false
joy2down = false
joy2left = false
joy2right = false
joy2fire = false
}
}

View File

@ -87,7 +87,7 @@ class C64Machine(title: String) : IVirtualMachine {
val txttab = ram[0x2b]+256*ram[0x2c] // basic load address ($0801 usually)
val fnaddr = ram[0xbb]+256*ram[0xbc] // file name address
return if (fnlen > 0) {
val filename = (0 until fnlen).map { ram[fnaddr+it].toChar() }.joinToString("")
val filename = (0 until fnlen).map { ram[fnaddr+it].toInt().toChar() }.joinToString("")
val loadEndAddress = searchAndLoadFile(filename, fa, sa, txttab)
if (loadEndAddress != null) {
ram[0x90] = 0 // status OK
@ -108,7 +108,7 @@ class C64Machine(title: String) : IVirtualMachine {
val fromAddr = ram[cpu.regA]+256*ram[cpu.regA+1]
val endAddr = cpu.regX+256*cpu.regY
val data = (fromAddr..endAddr).map { ram[it].toByte() }.toByteArray()
var filename = (0 until fnlen).map { ram[fnaddr+it].toChar() }.joinToString("").toLowerCase()
var filename = (0 until fnlen).map { ram[fnaddr+it].toInt().toChar() }.joinToString("").lowercase()
if (!filename.endsWith(".prg")) filename += ".prg"
File(filename).outputStream().use {
it.write(fromAddr and 0xff)
@ -133,13 +133,13 @@ class C64Machine(title: String) : IVirtualMachine {
"$" -> {
// load the directory
val files = File(".").listFiles(FileFilter { it.isFile })!!.associate {
val name = it.nameWithoutExtension.toUpperCase()
val ext = it.extension.toUpperCase()
val name = it.nameWithoutExtension.uppercase()
val ext = it.extension.uppercase()
val fileAndSize = Pair(it, it.length())
if (name.isEmpty()) Pair(".$ext", "") to fileAndSize
else Pair(name, ext) to fileAndSize
}
val dirname = File(".").canonicalPath.substringAfterLast(File.separator).toUpperCase()
val dirname = File(".").canonicalPath.substringAfterLast(File.separator).uppercase()
val dirlisting = makeDirListing(dirname, files, basicLoadAddress)
ram.load(dirlisting, basicLoadAddress)
return basicLoadAddress+dirlisting.size-1
@ -147,7 +147,7 @@ class C64Machine(title: String) : IVirtualMachine {
else -> {
fun findHostFile(filename: String): String? {
val file = File(".").listFiles(FileFilter { it.isFile })?.firstOrNull {
it.name.toUpperCase() == filename
it.name.uppercase() == filename
}
return file?.name
}
@ -179,7 +179,7 @@ class C64Machine(title: String) : IVirtualMachine {
listing.add((address ushr 8).toShort())
listing.add((lineNumber and 0xff).toShort())
listing.add((lineNumber ushr 8).toShort())
listing.addAll(line.map { it.toShort() })
listing.addAll(line.map { it.code.toShort() })
listing.add(0)
}
addLine(0, "\u0012\"${dirname.take(16).padEnd(16)}\" 00 2A")
@ -216,6 +216,11 @@ class C64Machine(title: String) : IVirtualMachine {
while (cpu.instrCycles > 0) bus.clock()
}
override fun reset() {
bus.reset()
hostDisplay.reset()
}
override fun executeMonitorCommand(command: String) = monitor.command(command)
fun start() {
@ -252,7 +257,7 @@ class C64Machine(title: String) : IVirtualMachine {
fun determineRomPath(): Path {
val candidates = listOf("./roms", "~/roms/c64", "~/roms", "~/.vice/C64")
val candidates = listOf("./roms", "~/roms/c64", "~/roms", "~/.vice/C64", "/usr/lib/vice/C64")
candidates.forEach {
val path = Paths.get(expandUser(it))
if (path.toFile().isDirectory) return path

View File

@ -182,6 +182,7 @@ class DebugWindow(private val vm: IVirtualMachine) : JFrame("Debugger - ksim65 v
}
"reset" -> {
vm.reset()
vm.bus.reset()
updateCpu(vm.cpu, vm.bus)
}

View File

@ -1,57 +1,27 @@
package razorvine.examplemachines
import razorvine.fonts.PsfFont
import razorvine.ksim65.*
import java.awt.*
import java.awt.event.*
import java.awt.image.BufferedImage
import java.util.*
import javax.imageio.ImageIO
import javax.swing.*
import javax.swing.event.MouseInputListener
/**
* Define a monochrome screen that can display 640x480 pixels
* and/or 80x30 characters (these are 8x16 pixels).
* Define a monochrome screen that can display 80x30 charaacters
* (usually equivalent to 640x480 pixels, but depends on the font size)
*/
object ScreenDefs {
const val SCREEN_WIDTH_CHARS = 80
const val SCREEN_HEIGHT_CHARS = 30
const val SCREEN_WIDTH = SCREEN_WIDTH_CHARS*8
const val SCREEN_HEIGHT = SCREEN_HEIGHT_CHARS*16
const val PIXEL_SCALING = 1.5
const val COLUMNS = 80
const val ROWS = 30
const val BORDER_SIZE = 32
val BG_COLOR = Color(0, 10, 20)
val FG_COLOR = Color(200, 255, 230)
val BORDER_COLOR = Color(20, 30, 40)
val Characters = loadCharacters()
private fun loadCharacters(): Array<BufferedImage> {
val img = ImageIO.read(javaClass.getResourceAsStream("/charset/unscii8x16.png"))
val charactersImage = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
charactersImage.createGraphics().drawImage(img, 0, 0, null)
val black = Color(0, 0, 0).rgb
val foreground = FG_COLOR.rgb
val nopixel = Color(0, 0, 0, 0).rgb
for (y in 0 until charactersImage.height) {
for (x in 0 until charactersImage.width) {
val col = charactersImage.getRGB(x, y)
if (col == black) charactersImage.setRGB(x, y, nopixel)
else charactersImage.setRGB(x, y, foreground)
}
}
val numColumns = charactersImage.width/8
val charImages = (0..255).map {
val charX = it%numColumns
val charY = it/numColumns
charactersImage.getSubimage(charX*8, charY*16, 8, 16)
}
return charImages.toTypedArray()
}
}
private class BitmapScreenPanel : JPanel() {
@ -61,15 +31,21 @@ private class BitmapScreenPanel : JPanel() {
private var cursorX: Int = 0
private var cursorY: Int = 0
private var cursorState: Boolean = false
private val screenFont = PsfFont("spleen-12x24") // nice fonts: sun12x22, iso01-12x22, ter-124b, spleen-12x24, default8x16
private val pixelScaling: Double = if(screenFont.width <= 8) 1.5 else 1.0
private val screenFontImage: BufferedImage
init {
println("SCREENFONT WIDTH: ${screenFont.width}")
val ge = GraphicsEnvironment.getLocalGraphicsEnvironment()
val gd = ge.defaultScreenDevice.defaultConfiguration
image = gd.createCompatibleImage(ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT, Transparency.OPAQUE)
image = gd.createCompatibleImage(ScreenDefs.COLUMNS*screenFont.width, ScreenDefs.ROWS*screenFont.height, Transparency.OPAQUE)
g2d = image.graphics as Graphics2D
screenFontImage = screenFont.convertToImage(g2d, ScreenDefs.FG_COLOR)
val size = Dimension((image.width*ScreenDefs.PIXEL_SCALING).toInt(),
(image.height*ScreenDefs.PIXEL_SCALING).toInt())
val size = Dimension((image.width*pixelScaling).toInt(),
(image.height*pixelScaling).toInt())
minimumSize = size
maximumSize = size
preferredSize = size
@ -82,13 +58,13 @@ private class BitmapScreenPanel : JPanel() {
override fun paint(graphics: Graphics) {
val g2d = graphics as Graphics2D
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR)
g2d.drawImage(image, 0, 0, (image.width*ScreenDefs.PIXEL_SCALING).toInt(),
(image.height*ScreenDefs.PIXEL_SCALING).toInt(), null)
g2d.drawImage(image, 0, 0, (image.width*pixelScaling).toInt(),
(image.height*pixelScaling).toInt(), null)
if (cursorState) {
val scx = (cursorX*ScreenDefs.PIXEL_SCALING*8).toInt()
val scy = (cursorY*ScreenDefs.PIXEL_SCALING*16).toInt()
val scw = (8*ScreenDefs.PIXEL_SCALING).toInt()
val sch = (16*ScreenDefs.PIXEL_SCALING).toInt()
val scx = (cursorX*pixelScaling*screenFont.width).toInt()
val scy = (cursorY*pixelScaling*screenFont.height).toInt()
val scw = (screenFont.width*pixelScaling).toInt()
val sch = (screenFont.height*pixelScaling).toInt()
g2d.setXORMode(Color.CYAN)
g2d.fillRect(scx, scy, scw, sch)
g2d.setPaintMode()
@ -98,7 +74,7 @@ private class BitmapScreenPanel : JPanel() {
fun clearScreen() {
g2d.background = ScreenDefs.BG_COLOR
g2d.clearRect(0, 0, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT)
g2d.clearRect(0, 0, ScreenDefs.COLUMNS*screenFont.width, ScreenDefs.ROWS*screenFont.height)
cursorPos(0, 0)
}
@ -110,20 +86,26 @@ private class BitmapScreenPanel : JPanel() {
fun getPixel(x: Int, y: Int) = image.getRGB(x, y) != ScreenDefs.BG_COLOR.rgb
fun setChar(x: Int, y: Int, character: Char) {
g2d.clearRect(8*x, 16*y, 8, 16)
val coloredImage = ScreenDefs.Characters[character.toInt()]
g2d.drawImage(coloredImage, 8*x, 16*y, null)
val charnum = character.code
val cx = charnum % (screenFontImage.width/screenFont.width)
val cy = charnum / (screenFontImage.width/screenFont.width)
g2d.clearRect(x*screenFont.width, y*screenFont.height, screenFont.width, screenFont.height)
g2d.drawImage(screenFontImage, x*screenFont.width, y*screenFont.height, (x+1)*screenFont.width,
(y+1)*screenFont.height, cx*screenFont.width, cy*screenFont.height,
(cx+1)*screenFont.width, (cy+1)*screenFont.height, null)
}
fun scrollUp() {
g2d.copyArea(0, 16, ScreenDefs.SCREEN_WIDTH, ScreenDefs.SCREEN_HEIGHT-16, 0, -16)
g2d.copyArea(0, screenFont.height,
ScreenDefs.COLUMNS*screenFont.width, (ScreenDefs.ROWS-1)*screenFont.height,
0, -screenFont.height)
g2d.background = ScreenDefs.BG_COLOR
g2d.clearRect(0, ScreenDefs.SCREEN_HEIGHT-16, ScreenDefs.SCREEN_WIDTH, 16)
g2d.clearRect(0, (ScreenDefs.ROWS-1)*screenFont.height, ScreenDefs.COLUMNS*screenFont.width, screenFont.height)
}
fun mousePixelPosition(): Point? {
val pos = mousePosition ?: return null
return Point((pos.x/ScreenDefs.PIXEL_SCALING).toInt(), (pos.y/ScreenDefs.PIXEL_SCALING).toInt())
return Point((pos.x/pixelScaling).toInt(), (pos.y/pixelScaling).toInt())
}
fun cursorPos(x: Int, y: Int) {
@ -242,4 +224,8 @@ class MainWindow(title: String) : JFrame(title), KeyListener, MouseInputListener
else keyboardBuffer.pop()
}
fun reset() {
// when the reset button is pressed
}
}

View File

@ -21,7 +21,7 @@ class EhBasicMachine(title: String) {
val rom = Rom(0xc000, 0xffff).also { it.load(javaClass.getResourceAsStream("/ehbasic_C000.bin").readBytes()) }
private val hostDisplay = MainWindow(title)
private val display = Display(0xd000, 0xd00a, hostDisplay, ScreenDefs.SCREEN_WIDTH_CHARS, ScreenDefs.SCREEN_HEIGHT_CHARS)
private val display = Display(0xd000, 0xd00a, hostDisplay, ScreenDefs.COLUMNS, ScreenDefs.ROWS)
private val keyboard = Keyboard(0xd400, 0xd400, hostDisplay)
private var paused = false

View File

@ -20,7 +20,7 @@ class VirtualMachine(title: String) : IVirtualMachine {
private val monitor = Monitor(bus, cpu)
private val debugWindow = DebugWindow(this)
private val hostDisplay = MainWindow(title)
private val display = Display(0xd000, 0xd00a, hostDisplay, ScreenDefs.SCREEN_WIDTH_CHARS, ScreenDefs.SCREEN_HEIGHT_CHARS)
private val display = Display(0xd000, 0xd00a, hostDisplay, ScreenDefs.COLUMNS, ScreenDefs.ROWS)
private val mouse = Mouse(0xd300, 0xd305, hostDisplay)
private val keyboard = Keyboard(0xd400, 0xd400, hostDisplay)
private var paused = false
@ -65,6 +65,11 @@ class VirtualMachine(title: String) : IVirtualMachine {
while (cpu.instrCycles > 0) bus.clock()
}
override fun reset() {
bus.reset()
hostDisplay.reset()
}
override fun executeMonitorCommand(command: String) = monitor.command(command)
fun start() {

View File

@ -0,0 +1,138 @@
package razorvine.fonts
import java.awt.Color
import java.awt.Graphics2D
import java.awt.Transparency
import java.awt.image.BufferedImage
import java.io.File
import java.io.FileInputStream
import java.io.IOException
import java.util.zip.GZIPInputStream
class PsfFont(name: String) {
// font format info: https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
val numChars: Int
val bytesPerChar: Int
val height: Int
val width: Int
private val hasUnicodeTable: Boolean
private val rawBitmaps: List<ByteArray>
init {
val data: ByteArray
val fontsDirectory = "/usr/share/kbd/consolefonts"
var stream = javaClass.getResourceAsStream("/charset/$name.psfu.gz") ?:
javaClass.getResourceAsStream("/charset/$name.psf.gz") ?:
(if(File("$fontsDirectory/$name.psfu.gz").exists()) FileInputStream("$fontsDirectory/$name.psfu.gz") else null ) ?:
(if(File("$fontsDirectory/$name.psf.gz").exists()) FileInputStream("$fontsDirectory/$name.psf.gz") else null ) ?:
(if(File("$fontsDirectory/$name.fnt.gz").exists()) FileInputStream("$fontsDirectory/$name.fnt.gz") else null )
if(stream==null) {
stream = javaClass.getResourceAsStream("/charset/$name.psfu") ?:
javaClass.getResourceAsStream("/charset/$name.psf") ?:
(if(File("$fontsDirectory/$name.psfu").exists()) FileInputStream("$fontsDirectory/$name.psfu") else null ) ?:
(if(File("$fontsDirectory/$name.psf").exists()) FileInputStream("$fontsDirectory/$name.psf") else null ) ?:
(if(File("$fontsDirectory/$name.fnt").exists()) FileInputStream("$fontsDirectory/$name.fnt") else null ) ?:
throw IOException("no such font: $name")
data = stream.readBytes()
} else {
GZIPInputStream(stream).use { data = it.readBytes() }
}
stream.close()
if (data[0] == 0x36.toByte() && data[1] == 0x04.toByte()) {
// continue reading PSF1 font
val mode = data[2].toInt()
numChars = if (mode and 1 != 0) 512 else 256
bytesPerChar = data[3].toInt()
hasUnicodeTable = mode and 2 != 0
height = bytesPerChar
width = 8
rawBitmaps = (0..numChars).map {
data.sliceArray(3+it*bytesPerChar..3+(it+1)*bytesPerChar)
}
// ignore unicode table for now: val table = stream.readAllBytes()
} else {
if (data[0] == 0x72.toByte() && data[1] == 0xb5.toByte() && data[2] == 0x4a.toByte() && data[3] == 0x86.toByte()) {
// continue reading PSF2 font
// skip the version val version = makeInt(data, 4)
val headersize = makeInt(data, 8)
val flags = makeInt(data, 12)
hasUnicodeTable = flags and 1 != 0
numChars = makeInt(data, 16)
bytesPerChar = makeInt(data, 20)
height = makeInt(data, 24)
width = makeInt(data, 28)
rawBitmaps = (0..numChars).map {
data.sliceArray(headersize+it*bytesPerChar..headersize+(it+1)*bytesPerChar)
}
} else {
hasUnicodeTable = false
numChars = 0
bytesPerChar = 0
height = 0
width = 0
rawBitmaps = emptyList()
}
}
}
fun convertToImage(gfx: Graphics2D, textColor: Color): BufferedImage {
// create a single image with all the characters in a vertical column from top to bottom.
val bitmap = gfx.deviceConfiguration.createCompatibleImage((width+7) and 0b11111000, height*numChars, Transparency.BITMASK)
val bytesHoriz = (width+7)/8
val color = textColor.rgb
val nopixel = Color(0, 0, 0, 0).rgb
for (char in 0 until numChars) {
for (b in 0 until bytesPerChar) {
val c = rawBitmaps[char][b].toInt()
val ix = 8*(b%bytesHoriz)
val iy = b/bytesHoriz+char*height
bitmap.setRGB(ix, iy, if (c and 0b10000000 != 0) color else nopixel)
bitmap.setRGB(ix+1, iy, if (c and 0b01000000 != 0) color else nopixel)
bitmap.setRGB(ix+2, iy, if (c and 0b00100000 != 0) color else nopixel)
bitmap.setRGB(ix+3, iy, if (c and 0b00010000 != 0) color else nopixel)
bitmap.setRGB(ix+4, iy, if (c and 0b00001000 != 0) color else nopixel)
bitmap.setRGB(ix+5, iy, if (c and 0b00000100 != 0) color else nopixel)
bitmap.setRGB(ix+6, iy, if (c and 0b00000010 != 0) color else nopixel)
bitmap.setRGB(ix+7, iy, if (c and 0b00000001 != 0) color else nopixel)
}
}
return bitmap
}
private fun makeInt(bytes: ByteArray, offset: Int) =
makeInt(bytes[offset], bytes[offset+1], bytes[offset+2], bytes[offset+3])
private fun makeInt(b0: Byte, b1: Byte, b2: Byte, b3: Byte) =
b0.toInt() or (b1.toInt() shl 8) or (b2.toInt() shl 16) or (b3.toInt() shl 24)
}
// private fun loadFallbackCharacters(): Array<BufferedImage> {
// val img = ImageIO.read(javaClass.getResourceAsStream("/charset/unscii8x16.png"))
// val charactersImage = BufferedImage(img.width, img.height, BufferedImage.TYPE_INT_ARGB)
// charactersImage.createGraphics().drawImage(img, 0, 0, null)
//
// val black = Color(0, 0, 0).rgb
// val foreground = FG_COLOR.rgb
// val nopixel = Color(0, 0, 0, 0).rgb
// for (y in 0 until charactersImage.height) {
// for (x in 0 until charactersImage.width) {
// val col = charactersImage.getRGB(x, y)
// if (col == black) charactersImage.setRGB(x, y, nopixel)
// else charactersImage.setRGB(x, y, foreground)
// }
// }
//
// val numColumns = charactersImage.width/8
// val charImages = (0..255).map {
// val charX = it%numColumns
// val charY = it/numColumns
// charactersImage.getSubimage(charX*8, charY*16, 8, 16)
// }
//
// return charImages.toTypedArray()
// }

View File

@ -87,7 +87,7 @@ class Assembler(cpu: Cpu6502, val memory: MemMappedComponent, initialStartAddres
}
val instructionSize: Int
val mnemonic = args[0].toLowerCase().trim()
val mnemonic = args[0].lowercase().trim()
when (args.size) {
1 -> {
// implied or acc

View File

@ -188,9 +188,7 @@ open class Cpu6502 : BusComponent() {
regPC++
instrCycles = currentInstruction.cycles
val extraCycleFromAddr = applyAddressingMode(currentInstruction.mode)
val extraCycleFromInstr = dispatchOpcode(currentOpcode)
if(extraCycleFromAddr and extraCycleFromInstr)
if(applyAddressingMode(currentInstruction.mode) and dispatchOpcode(currentOpcode))
instrCycles++
}
@ -628,6 +626,8 @@ open class Cpu6502 : BusComponent() {
}
protected open fun dispatchOpcode(opcode: Int): Boolean {
// note: this giant when-statement seems to be the fastest way of doing a jump table like this.
// experiments with an indexed array with function-pointers proved to be several times slower.
return when (opcode) {
0x00 -> iBrk()
0x01 -> iOra()
@ -1406,11 +1406,14 @@ open class Cpu6502 : BusComponent() {
return false
}
// unofficial/illegal 6502 instructions
// see http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
// or https://github.com/quietust/nintendulator/blob/master/src/CPU.cpp (search for LogBadOps)
// TODO: some of these may be implemented incorrectly / are not finished yet
// unofficial/illegal 6502 instructions now follow
// see http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
// and http://unusedino.de/ec64/technical/aay/c64/ibmain.htm
// or https://github.com/quietust/nintendulator/blob/master/src/CPU.cpp (search for LogBadOps)
// or https://github.com/stardot/b-em/blob/master/src/6502.c
// or https://github.com/wpmed92/MedNES/blob/master/NES/6502.cpp
// TODO: some of the instructions below may be implemented incorrectly / are not finished yet
private fun iAhx(): Boolean {
val addrHi = 0xff // TODO get the correct byte from the instruction (=last byte read)

View File

@ -7,6 +7,7 @@ import java.io.File
interface IVirtualMachine {
fun step()
fun pause(paused: Boolean)
fun reset()
fun getZeroAndStackPages(): Array<UByte>
fun loadFileInRam(file: File, loadAddress: Address?)

View File

@ -34,7 +34,7 @@ class Monitor(val bus: Bus, val cpu: Cpu6502) {
val result = mutableListOf<String>()
for (addr in start until end step 16) {
result.add("m$${hexW(addr)} "+(0..15).joinToString(" ") { hexB(bus.read(addr+it)) }+" "+(0..15).joinToString("") {
val chr = bus.read(addr+it).toChar()
val chr = bus.read(addr+it).toInt().toChar()
if (chr.isLetterOrDigit()) chr.toString()
else "."
})
@ -55,7 +55,7 @@ class Monitor(val bus: Bus, val cpu: Cpu6502) {
val result = mutableListOf<String>()
for (addr in start until end step 64) {
result.add("i$${hexW(addr)} "+(0..63).joinToString("") {
val chr = bus.read(addr+it).toChar()
val chr = bus.read(addr+it).toInt().toChar()
if (chr.isLetterOrDigit()) chr.toString()
else "."
})

View File

@ -2,20 +2,16 @@ package razorvine.ksim65
import razorvine.ksim65.components.Address
fun hexW(number: Address, allowSingleByte: Boolean = false): String {
val msb = number ushr 8
val lsb = number and 0xff
return if (msb == 0 && allowSingleByte) hexB(lsb)
else hexB(msb)+hexB(lsb)
}
fun hexW(number: Int, allowSingleByte: Boolean = false) =
if(allowSingleByte && (number and 0xff00 == 0)) {
number.toString(16).padStart(2, '0')
} else {
number.toString(16).padStart(4, '0')
}
fun hexB(number: Short): String = hexB(number.toInt())
fun hexB(number: Int): String {
val hexdigits = "0123456789abcdef"
val loNibble = number and 15
val hiNibble = (number and 65535) ushr 4
return hexdigits[hiNibble].toString()+hexdigits[loNibble]
}
fun hexB(number: Int) = number.toString(16).padStart(2, '0')
fun hexB(number: Short) = hexB(number.toInt())
typealias BreakpointHandler = (cpu: Cpu6502, pc: Address) -> Cpu6502.BreakpointResultAction

View File

@ -45,7 +45,7 @@ abstract class MemMappedComponent(val startAddress: Address, val endAddress: Add
}
print(" ")
val chars = if (charmapper != null) bytes.map { b -> charmapper(b) }
else bytes.map { b -> if (b in 32..255) b.toChar() else '.' }
else bytes.map { b -> if (b in 32..255) b.toInt().toChar() else '.' }
println(chars.joinToString(""))
}
}

View File

@ -52,7 +52,7 @@ class Display(startAddress: Address, endAddress: Address, private val host: IHos
}
override fun reset() {
charMatrix.forEach { it.fill(' '.toShort()) }
charMatrix.forEach { it.fill(' '.code.toShort()) }
cursorX = 0
cursorY = 0
charposX = 0
@ -94,7 +94,7 @@ class Display(startAddress: Address, endAddress: Address, private val host: IHos
0x02 -> {
if (charposY in 0 until charHeight && charposX in 0 until charWidth) {
charMatrix[charposY][charposX] = data
host.setChar(charposX, charposY, data.toChar())
host.setChar(charposX, charposY, data.toInt().toChar())
}
}
0x03 -> pixelX = (pixelX and 0xff00) or data.toInt()
@ -102,7 +102,7 @@ class Display(startAddress: Address, endAddress: Address, private val host: IHos
0x05 -> pixelY = (pixelY and 0xff00) or data.toInt()
0x06 -> pixelY = (pixelY and 0x00ff) or (data.toInt() shl 8)
0x07 -> {
if (pixelX in 0 until ScreenDefs.SCREEN_WIDTH && pixelY in 0 until ScreenDefs.SCREEN_HEIGHT) {
if (pixelX in 0 until ScreenDefs.COLUMNS*charWidth && pixelY in 0 until ScreenDefs.ROWS*charHeight) {
if (data == 0.toShort()) host.clearPixel(pixelX, pixelY)
else host.setPixel(pixelX, pixelY)
}
@ -121,7 +121,7 @@ class Display(startAddress: Address, endAddress: Address, private val host: IHos
cursorX = charWidth-1
}
}
charMatrix[cursorY][cursorX] = ' '.toShort()
charMatrix[cursorY][cursorX] = ' '.code.toShort()
host.setChar(cursorX, cursorY, ' ')
}
0x09 -> {
@ -142,7 +142,7 @@ class Display(startAddress: Address, endAddress: Address, private val host: IHos
else -> {
// set character on screen
charMatrix[cursorY][cursorX] = data
host.setChar(cursorX, cursorY, data.toChar())
host.setChar(cursorX, cursorY, data.toInt().toChar())
cursorX++
if (cursorX >= charWidth) {
cursorX = 0
@ -164,7 +164,7 @@ class Display(startAddress: Address, endAddress: Address, private val host: IHos
charMatrix[y+1].copyInto(charMatrix[y])
}
for (x in 0 until charWidth) {
charMatrix[charHeight-1][x] = ' '.toShort()
charMatrix[charHeight-1][x] = ' '.code.toShort()
}
cursorY--
host.scrollUp()

View File

@ -25,7 +25,7 @@ class Keyboard(startAddress: Address, endAddress: Address, private val host: IHo
override operator fun get(offset: Int): UByte {
return when (offset) {
0x00 -> host.keyboard()?.toShort() ?: 0
0x00 -> host.keyboard()?.code?.toShort() ?: 0
else -> 0xff
}
}

View File

@ -27,7 +27,7 @@ class ParallelPort(startAddress: Address, endAddress: Address) : MemMappedCompon
if (offset == 0) dataByte = data
else if (offset == 1) {
if ((data.toInt() and 1) == 1) {
val char = dataByte.toChar()
val char = dataByte.toInt().toChar()
println("PARALLEL WRITE: '$char'")
}
}

View File

@ -6,7 +6,7 @@ import java.io.File
* A ROM chip (read-only memory).
*/
class Rom(startAddress: Address, endAddress: Address, initialData: Array<UByte>? = null) : MemoryComponent(startAddress, endAddress) {
override val data: Array<UByte> = initialData?.copyOf() ?: Array<UByte>(endAddress-startAddress+1) { 0 }
override val data: Array<UByte> = initialData?.copyOf() ?: Array(endAddress-startAddress+1) { 0 }
init {
require(endAddress-startAddress+1 == data.size) { "rom address range doesn't match size of data bytes" }

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,24 @@
Copyright (c) 2018-2020, Frederic Cambus
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1 +1 @@
version=1.8
version=1.10

View File

@ -148,19 +148,19 @@ class Test6502CpuBasics {
}
}
fun runBCDbeebTest(cpu: Cpu6502, testChoice: Char) {
private fun runBCDbeebTest(cpu: Cpu6502, testChoice: Char) {
// bcd test code from https://github.com/hoglet67/AtomSoftwareArchive/tree/master/tests/clark
val bus = Bus()
bus.add(cpu)
cpu.breakpointForBRK = { _, pc -> fail("brk instruction at \$${hexW(pc)}") }
cpu.addBreakpoint(0xffee) { cpu, pc ->
cpu.addBreakpoint(0xffee) { cpu2, pc ->
// OSWRCH write character
print("${cpu.regA.toChar()}")
print("${cpu2.regA.toChar()}")
Cpu6502.BreakpointResultAction()
}
cpu.addBreakpoint(0xffe0) { cpu, pc ->
cpu.addBreakpoint(0xffe0) { cpu2, pc ->
// OSRDCH read character
cpu.regA = testChoice.toInt()
cpu2.regA = testChoice.code
Cpu6502.BreakpointResultAction()
}
val ram = Ram(0, 0xffff)

View File

@ -4,6 +4,8 @@ import org.junit.jupiter.api.parallel.ExecutionMode
import kotlin.test.*
// TODO: implement the still missing illegal instructions and replace these tests with the 'real' runTest
// the instr_test_v5 from nesdev https://wiki.nesdev.com/w/index.php/Emulator_tests may be useful to test the instructions
@Execution(ExecutionMode.CONCURRENT)
class Test6502TestSuiteIllegalInstructions: FunctionalTestsBase() {
@ -279,61 +281,61 @@ class Test6502TestSuiteIllegalInstructions: FunctionalTestsBase() {
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testAlrb() {
runTest("alrb") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testAneb() {
runTest("aneb") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testArrb() {
runTest("arrb") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testLxab() {
runTest("lxab") // TODO fix something?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testSbxb() {
runTest("sbxb") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testShaay() {
runTest("shaay") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testShaiy() {
runTest("shaiy") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testShsay() {
runTest("shsay") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testShxay() {
runTest("shxay") // TODO fix?
}
@Test
@Disabled("this illegal instruction is probablyt not implemented correctly yet")
@Disabled("this illegal instruction is probably not implemented correctly yet")
fun testShyax() {
runTest("shyax") // TODO fix?
}

View File

@ -50,7 +50,7 @@ abstract class TestCommon6502 {
abstract fun createCpu(): Cpu6502
init {
mpu = createCpu()
mpu = this.createCpu()
bus.add(mpu)
bus.add(memory)
memory.fill(0xaa)

View File

@ -33,7 +33,7 @@ class TestDisassembler {
val cpu = Cpu65C02()
val disassembler = Disassembler(cpu)
val memory = Ram(0, 0x0fff)
val source = javaClass.classLoader.getResource("disassem_r65c02.bin").readBytes()
val source = javaClass.classLoader.getResource("disassem_r65c02.bin")?.readBytes()!!
memory.load(source, 0x0200)
val disassem = disassembler.disassemble(memory.data, 0x0200..0x0250, 0)
assertEquals(0x251, disassem.second)
@ -80,7 +80,7 @@ ${'$'}0250 00 brk""", result)
val cpu = Cpu65C02()
val disassembler = Disassembler(cpu)
val memory = Ram(0, 0x0fff)
val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin").readBytes()
val source = javaClass.classLoader.getResource("disassem_wdc65c02.bin")?.readBytes()!!
memory.load(source, 0x200)
val disassem = disassembler.disassemble(memory.data, 0x0200..0x0215, 0)
assertEquals(0x216, disassem.second)