openmsx-0.10.0/0000755000175000017500000000000012262345041014044 5ustar manuelmanuel00000000000000openmsx-0.10.0/GNUmakefile0000644000175000017500000000002712262345041016115 0ustar manuelmanuel00000000000000include build/entry.mk openmsx-0.10.0/configure0000755000175000017500000000171212262345041015754 0ustar manuelmanuel00000000000000#!/bin/sh # Probes the build environment (libs, headers). if [ -z "$MAKE" ] then # Look for GNU Make; prefer "gmake" over "make". (false ; echo "nothing:" | make -f - > /dev/null 2>&1) && MAKE=make (false ; echo "nothing:" | gmake -f - > /dev/null 2>&1) && MAKE=gmake else echo "Using value of MAKE environment variable: \"$MAKE\"." fi if [ -z "$MAKE" ] then echo 'Error: no Make found.' echo 'Please install GNU Make and put it in your path as "gmake" or "make",' echo 'or put its location in an environment variable named "MAKE".' exit 1 fi if [ "gmake" != "$MAKE" ] then if ! ( $MAKE --version > /dev/null 2>&1 \ && ($MAKE --version 2> /dev/null | grep "GNU Make" > /dev/null) ) then echo "Error: Make does not appear to be GNU Make." echo 'Please install GNU Make and put it in your path as "gmake" or "make",' echo 'or put its location in an environment variable named "MAKE".' exit 1 fi fi # Invoke actual probe. $MAKE -f build/main.mk probe openmsx-0.10.0/node.mk0000644000175000017500000000021712262345041015322 0ustar manuelmanuel00000000000000include build/node-start.mk SUBDIRS:= \ src build doc Contrib DIST:= \ GNUmakefile configure \ README \ share include build/node-end.mk openmsx-0.10.0/README0000644000175000017500000000254512262345041014732 0ustar manuelmanuel00000000000000---------------------------------------------------------------------------- openMSX - the MSX emulator that aims for perfection ---------------------------------------------------------------------------- openMSX comes with a set of HTML manuals that tell what you need to know to install, configure and run openMSX. You can find these manuals in the directory 'manual' inside the directory 'doc'. You can read them using a web browser. You can read what has changed in this and the previous releases in the release notes. You can find the release notes of this release in the file 'release-notes.txt' in the directory 'doc'. Highlights of previous releases can be found in 'release-history.txt'. All source code and other works that are part of, or distributed with openMSX are copyrighted by their respective authors. The file 'authors.txt' contains a list of people who made works for openMSX or contributed works to openMSX. Some source files contain a license notice; all other source files are licensed under the GNU Public License (GPL), of which you can find a copy in the file 'GPL.txt'. If you got a binary release of openMSX and are interested in the sources, please visit our home page: http://openmsx.sourceforge.net/ Happy MSX-ing! the openMSX developers ---------------------------------------------------------------------------- openmsx-0.10.0/build/0000755000175000017500000000000012262345041015143 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/flavour-lto.mk0000644000175000017500000000146712262345041017756 0ustar manuelmanuel00000000000000# This flavour enables link-time optimization (LTO). # This requires GCC 4.5 or higher; it's known to work on GCC 4.6.3. include build/flavour-opt.mk # Enable LTO for compiling and linking. # Don't bother with native code output in the compile phase, since new code # will be generated in the link phase. # Ideally LTO would be enabled for the 3rdparty libs as well, but I haven't # been able to make that work. COMPILE_FLAGS+=-flto LINK_FLAGS+=-flto=jobserver # Enable this line to speed up compilation. This is supported from gcc-4.7 # onward. Quote from the gcc manual: # -fno-fat-lto-objects improves compilation time over plain LTO, but # requires the complete toolchain to be aware of LTO. It requires a # linker with linker plugin support for basic functionality. #COMPILE_FLAGS+=-fno-fat-lto-objects openmsx-0.10.0/build/package-slackware/0000755000175000017500000000000012262345041020510 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/package-slackware/slack-desc0000644000175000017500000000175512262345041022454 0ustar manuelmanuel00000000000000# HOW TO EDIT THIS FILE: # The "handy ruler" below makes it easier to edit a package description. Line # up the first '|' above the ':' following the base package name, and the '|' on # the right side marks the last column you can put a character in. You must make # exactly 11 lines for the formatting to be correct. It's also customary to # leave one space after the ':'. |-----handy-ruler------------------------------------------------------| openmsx: openMSX - The MSX emulator that aims for perfection openmsx: openmsx: openMSX is an open source MSX emulator that tries to achieve openmsx: near-perfect emulation by using a novel emulation model. openmsx: openmsx: openMSX is in alpha state, which means that some things work but not openmsx: all features are implemented yet. Many emulation features are openmsx: implemented, but in terms of user interface it is rather bare bones, openmsx: unless you use the optional Graphical User Interface dubbed openMSX openmsx: Catapult. openmsx: openmsx-0.10.0/build/package-slackware/node.mk0000644000175000017500000000016112262345041021764 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ catapult.desktop openmsx.SlackBuild slack-desc include build/node-end.mk openmsx-0.10.0/build/package-slackware/catapult.desktop0000644000175000017500000000044612262345041023724 0ustar manuelmanuel00000000000000[Desktop Entry] Name=openMSX Catapult GenericName=MSX Emulator Comment=The MSX emulator that aims for perfection Comment[it]=L'emulatore MSX che mira alla perfezione Exec=catapult Terminal=false Type=Application Icon=/usr/share/pixmaps/catapult.xpm Categories=Emulator;GTK;Game; Encoding=UTF-8 openmsx-0.10.0/build/package-slackware/openmsx.SlackBuild0000755000175000017500000001677712262345041024165 0ustar manuelmanuel00000000000000!/bin/sh # # SlackBuild for openMSX # http://openmsx.sourceforge.net # By SukkoPera # 27/10/2005 # # Thanks a lot to CAT for his Slackware package cration # guide (http://www.slacky.it/misto/tutorial/spunleashed.txt) # # Check out # - http://www.sukkopera.tk # - http://www.slacky.it # # Notes: # - This script will make a package including both openMSX and Catapult. # Please edit it to suit your needs. # - You'll need catapult.desktop, slack-desc and the openMSX and Catapult # sources, of course. # Get the current and temporary directories CWD=`pwd` if [ "$TMP" = "" ]; then TMP=/tmp/pkg fi # Set up working directories if [ ! -d $TMP ] then mkdir -p $TMP fi # Some useful variables about the package NAME=`basename $0 .SlackBuild` PKG=$TMP/package-$NAME VERSION=0.6.0 #VERSION=`date +%Y%m%d` ARCH=i686 # Autodetection makes this pkg i686 BUILD=2suk SOURCEARCH=$NAME-$VERSION # Set compilation flags according to the architecture #case $ARCH in # "i686") # SLCKFLAGS="-O2 -march=i686" # ;; # "celeron") # SLCKFLAGS="-Os -march=pentium3" # ;; # "pentium3") # SLCKFLAGS="-O2 -march=pentium3" # ;; # "pentium4") # SLCKFLAGS="-O2 -march=pentium4" # ;; # "duron"|"sempron") # SLCKFLAGS="-Os -march=athlon-xp -m3dnow" # ;; # "athlonxp") # SLCKFLAGS="-O2 -march=athlon-xp -m3dnow" # ;; # "x86_64") # SLCKFLAGS="-O2 -march=athlon64" # ;; # "sparc") # SLCKFLAGS="-O2" # ;; # "supersparc") # SLCKFLAGS="-O2 -mcpu=supersparc" # ;; # *|"i486") # SLCKFLAGS="-O2 -march=i486 -mcpu=i686" # ;; #esac # Add flags common to all architectures #SLCKFLAGS="-pipe -fomit-frame-pointer $SLCKFLAGS" # Print a welcome screen echo "+-----------------------------------------------------------------------" echo "| $NAME-$VERSION" echo "+-----------------------------------------------------------------------" if [ -d $PKG ] then if [ "$1" == "--clean" ] then # Clean up a previous build and go on rm -rf $PKG $TMP/$NAME-$VERSION else # Warn the user and stop echo "Previous package build directory found, please remove it and" echo "restart the SlackBuild script: $PKG" exit 1 fi fi mkdir -p $PKG for i in usr/share/applications usr/share/pixmaps do echo "--- Creating directory $PKG/$i" mkdir -p $PKG/$i done # Decompress echo "------------------------- Uncompressing source -------------------------" cd $TMP tar zxvf $CWD/$SOURCEARCH.tar.gz #mv $SOURCEARCH $NAME-$VERSION cd $NAME-$VERSION # Build #echo "------------------------------ Configuring -----------------------------" # ./configure --prefix=/usr \ # --sysconfdir=/etc \ # --localstatedir=/var echo "------------------------------- Patching -------------------------------" # Set installation directory to what we want SUBDEST=/usr/share/openmsx sed -i "s:\(INSTALL_BASE\:=\).*:\1$SUBDEST:" build/custom.mk sed -i "s:\(SYMLINK_FOR_BINARY\:=\).*:\1false:" build/custom.mk echo "------------------------------ Compiling -------------------------------" #CFLAGS=$SLCKFLAGS \ #CXXFLAGS=$SLCKFLAGS \ make INSTALL_SHARE_DIR=$SUBDEST res=$? if [ $res -ne 0 ] then # make failed, we cannot continue exit $res fi echo "------------------------------ Installing ------------------------------" DOCSDIR=$PKG/usr/doc/$NAME-$VERSION make install OPENMSX_INSTALL=$PKG/$SUBDEST \ INSTALL_SHARE_DIR=$PKG/$SUBDEST \ INSTALL_BINARY_DIR=$PKG/usr/bin \ INSTALL_DOC_DIR=$DOCSDIR res=$? if [ $res -ne 0 ] then # make install failed, we cannot continue exit 1 fi # Doc echo "----------------- Copying documentation and other files ----------------" mkdir -p $DOCSDIR for i in AUTHORS GPL README TODO do # cat $i | gzip > $DOCSDIR/$i.gz cp -a $i $DOCSDIR done # GUI echo "--- Compiling Catapult" GUINAME=catapult GUIVERSION=0.6.0_R2 GUITNAME=openmsx-catapult GUITVERSION=0.6.0-R2 GUISOURCEARCH=$GUITNAME-$GUITVERSION # Decompress echo "------------------------- Uncompressing source -------------------------" cd $TMP tar zxvf $CWD/$GUISOURCEARCH.tar.gz mv $GUISOURCEARCH $GUINAME-$GUIVERSION cd $GUINAME-$GUIVERSION # Build #echo "------------------------------ Configuring -----------------------------" # ./configure --prefix=/usr \ # --sysconfdir=/etc \ # --localstatedir=/var echo "------------------------------- Patching -------------------------------" # Set installation directory to what we want GUISUBDEST=/usr/share/openmsx/catapult sed -i "s:\(INSTALL_BASE\:=\).*:\1$GUISUBDEST:" build/custom.mk sed -i "s:\(SYMLINK_FOR_BINARY\:=\).*:\1false:" build/custom.mk sed -i "s:\(CATAPULT_OPENMSX_BINARY\:=\).*:\1/usr/bin/openmsx:" build/custom.mk sed -i "s:\(CATAPULT_OPENMSX_SHARE\:=\).*:\1/usr/share/openmsx:" build/custom.mk echo "------------------------------ Compiling -------------------------------" #CFLAGS=$SLCKFLAGS \ #CXXFLAGS=$SLCKFLAGS \ make clean make res=$? if [ $res -ne 0 ] then # make failed, we cannot continue exit $res fi echo "------------------------------ Installing ------------------------------" DOCSDIR=$PKG/usr/doc/$NAME-$VERSION/$GUINAME-$GUIVERSION mkdir -p $DOCSDIR make install CATAPULT_INSTALL=$PKG/$GUISUBDEST \ INSTALL_BINARY_DIR=$PKG/usr/bin \ INSTALL_DOC_DIR=$DOCSDIR res=$? if [ $res -ne 0 ] then # make install failed, we cannot continue exit 1 fi # Doc echo "----------------- Copying documentation and other files ----------------" for i in AUTHORS GPL README do # cat $i | gzip > $DOCSDIR/$i.gz cp -a $i $DOCSDIR done # Clean a little find $PKG -name .cvsignore -exec rm -f {} \; # Gzip man pages #find $PKG/usr/man -name "*.[123456789]" -exec gzip -9 {} \; # SlackBuild stuff mkdir -p $PKG/usr/doc/$NAME-$VERSION/slackbuild cp $CWD/{$0,slack-desc} $PKG/usr/doc/$NAME-$VERSION/slackbuild # Icon and desktop entry (We're using a custom one because the included one is broken) cp $PKG/usr/share/openmsx/catapult/resources/icons/catapult.xpm $PKG/usr/share/pixmaps cp $CWD/catapult.desktop $PKG/usr/share/applications # Remove broken desktop file (not in $PKG!) rm /usr/share/applications/openMSX-Catapult.desktop # Strip binaries echo "--------------------------- Stripping binaries -------------------------" find $PKG -type f | xargs file | grep ELF | cut -f 1 -d : | xargs strip --strip-unneeded # Set permissions echo "--------------------------- Setting permissions ------------------------" chown -R root.bin $PKG/usr/bin $PKG/usr/sbin chown -R root.root $PKG/usr/doc chmod -R 755 $PKG/usr/doc/* find $PKG/usr/doc -type f -exec chmod 644 {} \; if [ -d $PKG/usr/share ] then chown -R root.root $PKG/usr/share chmod -R 755 $PKG/usr/share/* find $PKG/usr/share -type f -exec chmod 644 {} \; fi # Copy Slackware package files echo "--------------------- Copying Slackware package files ------------------" mkdir -p $PKG/install cat $CWD/slack-desc > $PKG/install/slack-desc #cp $CWD/doinst.sh $PKG/install #chmod 755 $PKG/install/doinst.sh #cat $CWD/slack-required > $PKG/install/slack-required # Create package echo "---------------------------- Creating package --------------------------" echo "Creating package" cd $PKG makepkg -l y -c n $CWD/$NAME-$VERSION-$ARCH-$BUILD.tgz # Clean up if [ "$1" = "--cleanup" ]; then echo "---------------------- Cleaning up working directory -------------------" rm -rf $TMP/$NAME-$VERSION rm -rf $PKG fi # Package created echo "Package creation finished!" openmsx-0.10.0/build/python-search.sh0000755000175000017500000000035312262345041020267 0ustar manuelmanuel00000000000000#!/bin/sh for name in python python2 python2.5 python2.6 python2.7 python2.8 python2.9 do $name -c 'import sys; sys.exit(not((2, 5) <= sys.version_info < (3, )))' \ 2> /dev/null if test $? -eq 0 then echo $name break fi done openmsx-0.10.0/build/flavour-opt-debug.mk0000644000175000017500000000027512262345041021042 0ustar manuelmanuel00000000000000# Generic optimisation flavour: # does not target any specific CPU. # Optimisation flags. CXXFLAGS+=-O3 -DNDEBUG \ -ffast-math -funroll-loops -g # Strip executable? OPENMSX_STRIP:=false openmsx-0.10.0/build/components2code.py0000644000175000017500000000215412262345041020621 0ustar manuelmanuel00000000000000# Creates the components header file. from components import iterComponents from makeutils import extractMakeVariables from outpututils import rewriteIfChanged import sys def iterComponentsHeader(probeMakePath): probeVars = extractMakeVariables(probeMakePath) buildComponents = set( component.makeName for component in iterComponents() if component.canBuild(probeVars) ) yield '// Automatically generated by build process.' yield '' yield '#ifndef COMPONENTS_HH' yield '#define COMPONENTS_HH' yield '' for component in iterComponents(): varName = component.makeName yield '#define COMPONENT_%s %d' % (varName, varName in buildComponents) yield '' yield 'namespace openmsx {' yield '' yield 'static const char* const BUILD_COMPONENTS = "%s";' \ % ' '.join(sorted(buildComponents)) yield '' yield '} // namespace openmsx' yield '' yield '#endif // COMPONENTS_HH' if __name__ == '__main__': if len(sys.argv) == 3: rewriteIfChanged(sys.argv[1], iterComponentsHeader(sys.argv[2])) else: print >> sys.stderr, ( 'Usage: python components2code.py COMPONENTS_HEADER PROBE_MAKE' ) sys.exit(2) openmsx-0.10.0/build/fileutils.py0000644000175000017500000001203312262345041017514 0ustar manuelmanuel00000000000000from os import altsep, chmod, mkdir, remove, sep, stat, walk from os.path import dirname, exists, isdir, isfile, islink, join as joinpath from shutil import copyfile try: from os import symlink except ImportError: def symlink(src, dst): # pylint: disable-msg=W0613 raise OSError('This platform does not support symbolic links') def scanTree(baseDir): '''Scans files and directories from the given base directory and iterates through the paths of the files and directories it finds; these paths are relative to the base directory. Directories will always be returned before any of the files they contain. The base directory itself is not returned. Hidden files and directories are not returned. All paths returned use the OS native separator character (os.sep), regardless of which separator characters were used in the arguments. Raises IOError if there is an I/O error scanning the base directory. ''' if altsep is not None: # Make sure all paths use the OS native separator, so we can safely # compare paths and have consistent error messages. baseDir = baseDir.replace(altsep, sep) baseDir = baseDir.rstrip(sep) if not isdir(baseDir): raise IOError('Directory "%s" does not exist' % baseDir) def escalate(ex): raise IOError( 'Error scanning directory entry "%s": %s' % (ex.filename, ex) ) for dirPath, dirNames, fileNames in walk(baseDir, onerror = escalate): # Skip hidden directories. # We are deleting items from the list we are iterating over; that # requires some extra care. index = 0 while index < len(dirNames): if dirNames[index].startswith('.'): del dirNames[index] else: index += 1 # Skip hidden files. fileNames = [ name for name in fileNames if not name.startswith('.') ] if dirPath == baseDir: relDir = '' else: assert dirPath.startswith(baseDir + sep), dirPath relDir = dirPath[len(baseDir) + 1 : ] yield relDir for fileName in fileNames: yield joinpath(relDir, fileName) def installDir(path): '''Creates the given path, excluding parent directories. The directory is created with permissions such that all users can read what is installed and only the owner can modify what is installed. If the given path already exists, nothing happens. ''' if not isdir(path): # We have to do chmod() separately because the "mode" argument of # mkdir() is modified by umask. mkdir(path) chmod(path, 0755) def _installDirsRec(path): '''Like installDirs(), except that "altsep" is not supported as directory separator in "path". ''' path = path.rstrip(sep) if not isdir(path): index = path.rfind(sep) if index != -1: _installDirsRec(path[ : index]) mkdir(path) chmod(path, 0755) def installDirs(path): '''Creates the given path, including any parent directories if necessary. Any newly created directories are created with permissions such that all users can read what is installed and only the owner can modify what is installed. If the given path already exists, nothing happens. ''' if altsep is not None: path = path.replace(altsep, sep) _installDirsRec(path) def installFile(srcPath, destPath): '''Copies a file from the given source path to the given destination path. The destination file is created with permissions such that all users can read (and execute, if appropriate) what is installed and only the owner can modify what is installed. Raises IOError if there is a problem reading or writing files. ''' copyfile(srcPath, destPath) chmod(destPath, 0755 if (stat(srcPath).st_mode & 0100) else 0644) def installSymlink(target, link): '''Creates a symbolic link with the given name to the given target. If a symbolic link with the given name already exists, it is replaced. If a different file type with the given name already exists, OSError is raised. Also raises OSError if this platform lacks os.symlink(). ''' if islink(link): remove(link) symlink(target, link) def installTree(srcDir, destDir, paths): '''Copies files and directories from the given source directory to the given destination directory. The given paths argument is a sequence of paths relative to the source directory; only those files and directories are copied. The scanTree() function is suitable to provide paths. Files and directories are created with permissions such that all users can read (and execute, if appropriate) what is installed and only the owner can modify what is installed. Raises IOError if there is a problem reading or writing files or directories. ''' if not isdir(destDir): raise IOError('Destination directory "%s" does not exist' % destDir) for relPath in paths: if altsep is not None: relPath = relPath.replace(altsep, sep) srcPath = joinpath(srcDir, relPath) destPath = joinpath(destDir, relPath) if islink(srcPath): print 'Skipping symbolic link:', srcPath elif isdir(srcPath): _installDirsRec(destPath) elif isfile(srcPath): _installDirsRec(dirname(destPath)) installFile(srcPath, destPath) elif exists(srcPath): print 'Skipping unknown kind of file system entry:', srcPath else: print 'Skipping non-existing path:', srcPath openmsx-0.10.0/build/3rdparty_packages2make.py0000644000175000017500000000401012262345041022036 0ustar manuelmanuel00000000000000from packages import getPackage, iterDownloadablePackages import sys def printPackagesMake(): patchesDir = 'build/3rdparty' sourceDir = 'derived/3rdparty/src' tarballsDir = 'derived/3rdparty/download' print 'SOURCE_DIR:=%s' % sourceDir print print '# Information about packages.' print '# Generated from the data in "build/packages.py".' print tarballs = [] for package in iterDownloadablePackages(): makeName = package.getMakeName() tarball = tarballsDir + '/' + package.getTarballName() tarballs.append(tarball) print '# %s' % package.niceName print 'PACKAGE_%s:=%s' % (makeName, package.getSourceDirName()) print 'TARBALL_%s:=%s' % (makeName, tarball) print '# Download:' print '%s:' % tarball print '\tmkdir -p %s' % tarballsDir print '\t$(PYTHON) build/download.py %s/%s %s' % ( package.downloadURL, package.getTarballName(), tarballsDir ) packageSourceDirName = package.getSourceDirName() packageSourceDir = sourceDir + '/' + packageSourceDirName patchFile = '%s/%s.diff' % (patchesDir, packageSourceDirName) print '# Verify:' verifyMarker = '%s.verified' % tarball print '%s: %s' % (verifyMarker, tarball) print '\t$(PYTHON) build/checksum.py %s %d %s' % ( tarball, package.fileLength, ' '.join('%s=%s' % item for item in package.checksums.iteritems()) ) print '\ttouch %s' % verifyMarker print '# Extract:' print '%s: %s' % (packageSourceDir, verifyMarker) print '\trm -rf %s' % packageSourceDir print '\tmkdir -p %s' % sourceDir print '\t$(PYTHON) build/extract.py %s %s %s' % ( tarball, sourceDir, packageSourceDirName ) print '\ttest ! -e %s || $(PYTHON) build/patch.py %s %s' % ( patchFile, patchFile, sourceDir ) print '\ttouch %s' % sourceDir print print '# Convenience target to download all source packages.' print '.PHONY: download' print 'download: %s' % ' '.join(tarballs) if __name__ == '__main__': if len(sys.argv) == 1: printPackagesMake() else: print >> sys.stderr, \ 'Usage: python 3rdparty_packages2make.py' sys.exit(2) openmsx-0.10.0/build/android_download.py0000644000175000017500000000064012262345041021024 0ustar manuelmanuel00000000000000from thirdparty_download import fetchPackageSource import sys def main(tarballsDir, sourcesDir, patchesDir): fetchPackageSource('TCL_ANDROID', tarballsDir, sourcesDir, patchesDir) if __name__ == '__main__': if len(sys.argv) == 1: main( 'derived/3rdparty/download', 'derived/3rdparty/src', 'build/3rdparty' ) else: print >> sys.stderr, ( 'Usage: python android_download.py' ) sys.exit(2) openmsx-0.10.0/build/components2defs.py0000644000175000017500000000134412262345041020630 0ustar manuelmanuel00000000000000# Generates the contents of "components_defs.mk". from components import EmulationCore, iterComponents from makeutils import extractMakeVariables from outpututils import rewriteIfChanged import sys def iterComponentDefs(probeMakePath): probeVars = extractMakeVariables(probeMakePath) yield '# Automatically generated by build process.' yield 'CORE_LIBS:=%s' % ' '.join(EmulationCore.dependsOn) for component in iterComponents(): yield 'COMPONENT_%s:=%s' % ( component.makeName, str(component.canBuild(probeVars)).lower() ) if len(sys.argv) == 3: rewriteIfChanged(sys.argv[1], iterComponentDefs(sys.argv[2])) else: print >> sys.stderr, ( 'Usage: python components2defs.py COMPONENTS_DEFS PROBE_MAKE' ) sys.exit(2) openmsx-0.10.0/build/cpu2flags.py0000644000175000017500000000061112262345041017401 0ustar manuelmanuel00000000000000from cpu import getCPU import sys def getCPUFlags(cpuName): cpu = getCPU(cpuName) return ' '.join(cpu.gccFlags) if __name__ == '__main__': if len(sys.argv) == 2: cpuName = sys.argv[1] try: print getCPUFlags(cpuName) except KeyError: print >> sys.stderr, 'Unknown CPU "%s"' % cpuName else: print >> sys.stderr, 'Usage: python cpu2flags.py OPENMSX_TARGET_CPU' sys.exit(2) openmsx-0.10.0/build/entry-seq.mk0000644000175000017500000000114612262345041017425 0ustar manuelmanuel00000000000000# Declares a dependency chain of sequence- on sequence, sequence- # on sequence etc, where N is the number of words in ACTION_COUNTER. .PHONY: sequence-$(words $(ACTION_COUNTER)) ifeq ($(words $(ACTION_COUNTER)),0) sequence-0: else NEXT_ACTION_COUNTER:=$(wordlist 2,999999,$(ACTION_COUNTER)) sequence-$(words $(ACTION_COUNTER)): sequence-$(words $(NEXT_ACTION_COUNTER)) @echo Action: $(word $(@:sequence-%=%),$(MAKECMDGOALS)) @$(MAKE) --no-print-directory -f build/main.mk \ $(word $(@:sequence-%=%),$(MAKECMDGOALS)) ACTION_COUNTER:=$(NEXT_ACTION_COUNTER) include build/entry-seq.mk endif openmsx-0.10.0/build/version.py0000644000175000017500000000445012262345041017205 0ustar manuelmanuel00000000000000# Contains the openMSX version number and versioning related functions. from executils import captureStdout from makeutils import filterLines from os import makedirs from os.path import isdir import re # Name used for packaging. packageName = 'openmsx' # Version number. packageVersionNumber = '0.10.0' # Note: suffix should be empty or with dash, like "-rc1" or "-test1" packageVersionSuffix = '' packageVersion = packageVersionNumber + packageVersionSuffix # Is this a release version ("True") or development version ("False"). releaseFlag = True def _extractRevisionFromStdout(log, command, regex): text = captureStdout(log, command) if text is None: # Error logging already done by captureStdout(). return None # pylint 0.18.0 somehow thinks captureStdout() returns a list, not a string. lines = text.split('\n') # pylint: disable-msg=E1103 for revision, in filterLines(lines, regex): print >> log, 'Revision number found by "%s": %s' % (command, revision) return revision else: print >> log, 'Revision number not found in "%s" output:' % command print >> log, text return None def extractGitRevision(log): return _extractRevisionFromStdout( log, 'git describe', r'\S+?-(\S+)$' ) def extractNumberFromGitRevision(revisionStr): if revisionStr is None: return None return re.match(r'(\d+)+', revisionStr).group(0) _cachedRevision = False # because None is a valid result def extractRevision(): global _cachedRevision if _cachedRevision is not False: return _cachedRevision if releaseFlag: # Not necessary, we do not append revision for a release build. return None if not isdir('derived'): makedirs('derived') log = open('derived/version.log', 'w') print >> log, 'Extracting revision info...' try: revision = extractGitRevision(log) print >> log, 'Revision string: %s' % revision print >> log, 'Revision number: %s' % extractNumberFromGitRevision(revision) finally: log.close() _cachedRevision = revision return revision def extractRevisionNumber(): return int(extractNumberFromGitRevision(extractRevision()) or 1) def extractRevisionString(): return extractRevision() or 'unknown' def getVersionedPackageName(): if releaseFlag: return '%s-%s' % (packageName, packageVersion) else: return '%s-%s-%s' % ( packageName, packageVersion, extractRevisionString() ) openmsx-0.10.0/build/flavour-android.mk0000644000175000017500000000030712262345041020570 0ustar manuelmanuel00000000000000# Android flavour. Copy all ANDROID_CXXFLAGS into TARGET_FLAGS # Otherwise, probe fails as it won't be able to find the include files TARGET_FLAGS+=$(ANDROID_CXXFLAGS) CXXFLAGS:=$(ANDROID_CXXFLAGS) openmsx-0.10.0/build/package-dingux/0000755000175000017500000000000012262345041020032 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/package-dingux/openmsx-start.sh0000755000175000017500000000040112262345041023210 0ustar manuelmanuel00000000000000#!/bin/sh # Startup script for openMSX. # Run this to start openMSX; running the executable directly does not work. export OPENMSX_SYSTEM_DATA=/usr/local/share/openmsx exec /usr/local/bin/openmsx.dge #exec /usr/local/bin/openmsx.dge &> /tmp/openmsx-log.txt openmsx-0.10.0/build/package-dingux/node.mk0000644000175000017500000000014512262345041021310 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ openmsx-start.sh README.txt zip.mk include build/node-end.mk openmsx-0.10.0/build/package-dingux/zip.mk0000644000175000017500000000164512262345041021173 0ustar manuelmanuel00000000000000# Create a zip file for Dingoo. LOCAL_DIR:=$(BINDIST_DIR)/local PACKAGE_SUPPORT_DIR:=build/package-dingux # Override install locations. INSTALL_BINARY_DIR:=$(LOCAL_DIR)/bin INSTALL_SHARE_DIR:=$(LOCAL_DIR)/share/openmsx INSTALL_DOC_DIR:=$(LOCAL_DIR)/doc/openmsx BINDIST_OPENMSX_START:=$(INSTALL_BINARY_DIR)/openmsx-start.sh BINDIST_README:=$(INSTALL_DOC_DIR)/README.txt PACKAGE_FULL:=$(shell PYTHONPATH=build $(PYTHON) -c \ "import version; print version.getVersionedPackageName()" \ ) BINDIST_PACKAGE:=$(PACKAGE_FULL)-dingux-bin.zip zip: install bindist: zip $(BINDIST_OPENMSX_START) $(BINDIST_README) @echo "Creating zip file:" cd $(BINDIST_DIR) && zip ../$(BINDIST_PACKAGE) -r local $(BINDIST_OPENMSX_START): $(PACKAGE_SUPPORT_DIR)/openmsx-start.sh install $(BINDIST_README): $(PACKAGE_SUPPORT_DIR)/README.txt install $(BINDIST_OPENMSX_START) $(BINDIST_README): @echo " Copying $(@F)..." @mkdir -p $(@D) @cp $< $@ openmsx-0.10.0/build/package-dingux/README.txt0000644000175000017500000000644212262345041021536 0ustar manuelmanuel00000000000000openMSX for Dingux ================== This text is a quick guide for the Dingoo port of openMSX. It is still a bit of an early port, so expect it to have some rough edges. For example, startup is slow (expect about 10 seconds). Especially important is to not add too many and especially too big system roms. This makes start up and loading states (the first time) loads slower. We are releasing it so people can play with it and give us feedback. Please help us make this a great way to play MSX games on a handheld. Contact info is at the bottom of this text. About Dingux ------------ Dingux is Linux for the Dingoo A-320 and possibly other similar devices in the future. About the Dingoo A-320: http://en.wikipedia.org/wiki/Dingoo Dingux blog: http://www.dingux.com/ Dingux downloads: http://code.google.com/p/dingoo-linux/downloads/list New Dingux site hub: http://dingoonity.org/ Installing Dingux ----------------- First, download the dual boot installer and follow the instructions in its README file. Next, download the Dingux system and copy it to your miniSD card as described in its README file. New Dingux system releases are made regularly and we will only test openMSX against the latest Dingux system release, so please keep your system updated. Installing openMSX ------------------ This info is also in the openMSX Compilation Guide: http://openmsx.sourceforge.net/manual/compile.html#installstandalone Copy the directory named "local" from the openMSX for Dingux ZIP file to the root of the miniSD card. If you have run Dingux from the miniSD card before, there will already be a directory named "local". You can safely merge both directories. openMSX comes with the C-BIOS system ROMs, which is an open source MSX BIOS. If you'd like to use other system ROMs, for example to get disk support or because you want to emulate the exact MSX model you had years ago, you have to install those yourself. Please read this for more info: http://openmsx.sourceforge.net/manual/setup.html#systemroms As mentioned before, currently it's wise to include only those system ROMs you really need. Better not install large ones either (e.g. the MoonSound ROM), unless you don't care about very long start up times. Using openMSX ------------- The keymappings are documented in the openMSX User's Manual: http://openmsx.sourceforge.net/manual/user.html#keymapping Customizing openMSX ------------------- openMSX can be configured using the settings.xml file. This file only stores settings that have a non-default value. Things like key bindings can be customized by writing short Tcl scripts. Please have a look in the share/openmsx/scripts directory for examples. Any script you place there will be executed by openMSX on startup. Please read this document to learn about the commands and settings of openMSX: http://openmsx.sourceforge.net/manual/commands.html Building openMSX ---------------- If you want to make bigger changes, you have to compile your own openMSX for Dingux. This is described in the Compilation Guide (with specific instructions for Dingux): http://openmsx.sourceforge.net/manual/compile.html Feedback -------- If you found a bug, made a patch, created a cool theme or have anything else you'd like to share, please contact us, see http://openmsx.sourceforge.net/manual/user.html#contact openmsx-0.10.0/build/patch.py0000644000175000017500000002241212262345041016615 0ustar manuelmanuel00000000000000# Applies a unified diff to a directory tree. from os.path import abspath, isdir, join as joinpath, sep import re import sys class LineScanner(object): def __init__(self, stream, lineFilter = None): '''Scans the given stream. Any object providing readline() can be passed as stream, such as file objects. The line scanner does not close the stream. The optional line filter is a function that will be called for every line read from the stream; iff the filter returns False, the line is skipped. This can be useful for example for skipping comment lines. ''' if not hasattr(stream, 'readline'): raise TypeError( 'Invalid type for stream: %s' % type(stream).__name__ ) self.__stream = stream self.__filter = lineFilter self.__currLine = None self.__lineNo = 0 self.next() def end(self): '''Returns True iff the end of the stream has been reached. ''' return self.__currLine is None def peek(self): '''Returns the current line. Like readline(), the returned string includes the newline characteter if it is present in the stream. ''' return self.__currLine def next(self): '''Moves on to the next line. Raises IOError if there is a problem reading from the stream. ''' stream = self.__stream lineFilter = self.__filter while True: line = stream.readline() self.__lineNo += 1 if line: if lineFilter is None or lineFilter(line): break else: line = None break self.__currLine = line def getLineNumber(self): '''Returns the line number of the current line. The first line read from the stream is considered line 1. ''' return self.__lineNo def parseError(self, msg, lineNo = None): '''Returns a ParseError object with a descriptive message. Raising the exception is left to the caller, to make sure the backtrace is meaningful. If a line number is given, that line number is used in the message, otherwise the current line number is used. ''' stream = self.__stream if lineNo is None: lineNo = self.getLineNumber() return ParseError( lineNo, 'Error parsing %s at line %d: %s' % ( '"%s"' % stream.name if hasattr(stream, 'name') else 'stream', lineNo, msg ) ) class ParseError(Exception): def __init__(self, lineNo, msg): Exception.__init__(self, msg) self.lineNo = lineNo class _Change(object): oldInc = None newInc = None action = None def __init__(self, content): self.content = content def __str__(self): return self.action + self.content.rstrip() class _Context(_Change): oldInc = 1 newInc = 1 action = ' ' class _Add(_Change): oldInc = 0 newInc = 1 action = '+' class _Remove(_Change): oldInc = 1 newInc = 0 action = '-' class Hunk(object): '''Contains the differences between two versions of a single section within a file. ''' reDeltaLine = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@') changeClasses = dict( (cc.action, cc) for cc in (_Context, _Add, _Remove) ) @classmethod def parse(cls, scanner): delta = cls.reDeltaLine.match(scanner.peek()) if delta is None: raise scanner.parseError('invalid delta line') oldLine, oldLen, newLine, newLen = ( int(n) for n in delta.groups() ) deltaLineNo = scanner.getLineNumber() scanner.next() def lineCountMismatch(oldOrNew, declared, actual): return scanner.parseError( 'hunk %s size mismatch: %d declared, %d actual' % ( oldOrNew, declared, actual ), deltaLineNo ) def iterChanges(): oldCount = 0 newCount = 0 while not scanner.end(): line = scanner.peek() if (line.startswith('---') or line.startswith('+++')) \ and oldCount == oldLen and newCount == newLen: # Hunks for a new file start here. # Since a change line could start with "---" or "+++" # as well we also have to check whether we are at the # declared end of this hunk. break if len(line) == 1: # Assume this is an empty context line that had its single # space character removed. line = ' ' changeClass = cls.changeClasses.get(line[0]) if changeClass is None: break content = line[1 : ] scanner.next() if not scanner.end() and scanner.peek().startswith('\\'): # No newline at end of file. assert content[-1] == '\n' content = content[ : -1] scanner.next() change = changeClass(content) yield change oldCount += change.oldInc newCount += change.newInc if oldCount != oldLen: raise lineCountMismatch('old', oldLen, oldCount) if newCount != newLen: raise lineCountMismatch('new', newLen, newCount) return cls(oldLine, newLine, iterChanges()) def __init__(self, oldLine, newLine, changes): self.__oldLine = oldLine self.__newLine = newLine self.__changes = tuple(changes) def __str__(self): return 'hunk(old=%d,new=%d)' % (self.__oldLine, self.__newLine) def getOldLine(self): return self.__oldLine def getNewLine(self): return self.__newLine def iterChanges(self): return iter(self.__changes) class Diff(object): '''Contains the differences between two versions of a single file. ''' @classmethod def load(cls, path): '''Iterates through the differences contained in the given diff file. Only the unified diff format is supported. Each element returned is a Diff object containing the differences to a single file. ''' inp = open(path, 'r') try: scanner = LineScanner(inp, lambda line: not line.startswith('#')) error = scanner.parseError def parseHunks(): while not scanner.end(): if scanner.peek().startswith('@@'): yield Hunk.parse(scanner) else: break while not scanner.end(): if scanner.peek().startswith('diff '): scanner.next() diffLineNo = scanner.getLineNumber() if not scanner.peek().startswith('--- '): raise error('"---" expected (not a unified diff?)') scanner.next() newFileLine = scanner.peek() if not newFileLine.startswith('+++ '): raise error('"+++" expected (not a unified diff?)') index = 4 length = len(newFileLine) while index < length and not newFileLine[index].isspace(): index += 1 filePath = newFileLine[4 : index] scanner.next() try: yield cls(filePath, parseHunks()) except ValueError, ex: raise error('inconsistent hunks: %s' % ex, diffLineNo) finally: inp.close() def __init__(self, path, hunks): self.__path = path self.__hunks = sorted(hunks, key = lambda hunk: hunk.getOldLine()) # Sanity check on line numbers. offset = 0 for hunk in self.__hunks: declaredOffset = hunk.getNewLine() - hunk.getOldLine() if offset != declaredOffset: raise ValueError( 'hunk offset mismatch: %d declared, %d actual' % ( declaredOffset, offset ) ) offset += sum( change.newInc - change.oldInc for change in hunk.iterChanges() ) def __str__(self): return 'diff of %d hunks to "%s"' % (len(self.__hunks), self.__path) def getPath(self): return self.__path def iterHunks(self): '''Iterates through the hunks in this diff in order of increasing old line numbers. ''' return iter(self.__hunks) def patch(diff, targetDir): '''Applies the given Diff to the given target directory. No fuzzy matching is done: if the diff does not apply exactly, ValueError is raised. ''' absTargetDir = abspath(targetDir) + sep absFilePath = abspath(joinpath(absTargetDir, diff.getPath())) if not absFilePath.startswith(absTargetDir): raise ValueError( 'Refusing to patch file "%s" outside destination directory' % diff.getPath() ) # Read entire file into memory. # The largest file we expect to patch is the "configure" script, which is # typically about 1MB. inp = open(absFilePath, 'r') try: lines = inp.readlines() finally: inp.close() for hunk in diff.iterHunks(): # We will be modifying "lines" at index "newLine", while "oldLine" is # only used to produce error messages if there is a context or remove # mismatch. As a result, "newLine" is 0-based and "oldLine" is 1-based. oldLine = hunk.getOldLine() newLine = hunk.getNewLine() - 1 for change in hunk.iterChanges(): if change.oldInc == 0: lines.insert(newLine, change.content) else: assert change.oldInc == 1 if change.content.rstrip() != lines[newLine].rstrip(): raise ValueError('mismatch at line %d' % oldLine) if change.newInc == 0: del lines[newLine] oldLine += change.oldInc newLine += change.newInc out = open(absFilePath, 'w') try: out.writelines(lines) finally: out.close() def main(diffPath, targetDir): try: differences = list(Diff.load(diffPath)) except IOError, ex: print >> sys.stderr, 'Error reading diff:', ex sys.exit(1) except ParseError, ex: print >> sys.stderr, ex sys.exit(1) if not isdir(targetDir): print >> sys.stderr, \ 'Destination directory "%s" does not exist' % targetDir sys.exit(1) for diff in differences: targetPath = joinpath(targetDir, diff.getPath()) try: patch(diff, targetDir) except IOError, ex: print >> sys.stderr, 'I/O error patching "%s": %s' % ( targetPath, ex ) sys.exit(1) except ValueError, ex: print >> sys.stderr, 'Patch could not be applied to "%s": %s' % ( targetPath, ex ) sys.exit(1) else: print 'Patched:', targetPath if __name__ == '__main__': if len(sys.argv) == 3: main(*sys.argv[1 : ]) else: print >> sys.stderr, \ 'Usage: python patch.py diff target' sys.exit(2) openmsx-0.10.0/build/node.mk0000644000175000017500000000047612262345041016430 0ustar manuelmanuel00000000000000include build/node-start.mk SUBDIRS:= \ 3rdparty package-darwin package-slackware msvc package-arch \ package-dingux package-windows android DIST:= \ main.mk node-end.mk node-start.mk entry.mk entry-seq.mk \ custom.mk 3rdparty.mk \ flavour-*.mk platform-*.mk \ *.py python-search.sh include build/node-end.mk openmsx-0.10.0/build/platform-darwin.mk0000644000175000017500000000322712262345041020606 0ustar manuelmanuel00000000000000# Configuration for creating a Darwin app folder. # In practice, this is used for Mac OS X; I'm not sure all of it applies to # other Darwin-based systems. # Does platform support symlinks? USE_SYMLINK:=true # The app folder will set a hi-res icon, so the openMSX process should not # replace this with its own low-res icon. SET_WINDOW_ICON:=false # Compile for the selected CPU. ifeq ($(OPENMSX_TARGET_CPU),x86) TARGET_FLAGS+=-arch i386 else TARGET_FLAGS+=-arch $(OPENMSX_TARGET_CPU) endif # File name extension of executables. EXEEXT:= LIBRARYEXT:=.so # Select the OS X version we want to be compatible with. # In theory it is possible to compile against an OS X version number lower # than the SDK version number, but in practice this doesn't seem to work # since libraries such as libxml2 can change soname between OS X versions. # Clang as shipped with Xcode requires OS X 10.7 or higher for compiling with # libc++, when compiling Clang and libc++ from source 10.6 works as well. OSX_VER:=10.7 TARGET_FLAGS+=-mmacosx-version-min=$(OSX_VER) # Select the SDK to use. XCODE_PATH:=$(shell xcode-select -print-path) ifneq ($(XCODE_PATH),) SDK_PATH:=$(XCODE_PATH)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX$(OSX_VER).sdk else # The SDK path for the older stand-alone Xcode: SDK_PATH:=/Developer/SDKs/MacOSX$(OSX_VER).sdk ifneq ($(shell [ -d $(SDK_PATH) ] && echo exists),exists) $(error No Mac OS X SDK found) endif endif $(info Using SDK: $(SDK_PATH)) TARGET_FLAGS+=-isysroot $(SDK_PATH) # Select Clang as the compiler and libc++ as the standard library. CXX:=clang++ COMPILE_FLAGS+=-stdlib=libc++ LINK_FLAGS+=-lc++ # Link against CoreMIDI. LINK_FLAGS+=-framework CoreMIDI openmsx-0.10.0/build/outpututils.py0000644000175000017500000000203312262345041020134 0ustar manuelmanuel00000000000000# Various utility functions for generating output files and directories. from os import makedirs from os.path import dirname, isdir, isfile def createDirFor(filePath): '''Creates an output directory for containing the given file path. Nothing happens if the directory already exsits. ''' dirPath = dirname(filePath) if dirPath and not isdir(dirPath): makedirs(dirPath) def rewriteIfChanged(path, lines): '''Writes the file with the given path if it does not exist yet or if its contents should change. The contents are given by the "lines" sequence. Returns True if the file was (re)written, False otherwise. ''' newLines = [ line + '\n' for line in lines ] if isfile(path): inp = open(path, 'r') try: oldLines = inp.readlines() finally: inp.close() if newLines == oldLines: print 'Up to date: %s' % path return False else: print 'Updating %s...' % path else: print 'Creating %s...' % path createDirFor(path) out = open(path, 'w') try: out.writelines(newLines) finally: out.close() return True openmsx-0.10.0/build/platform-android.mk0000644000175000017500000000145612262345041020744 0ustar manuelmanuel00000000000000# Configuration for Android, for ARM. # It *must* be called from Android SDL port build system. Toolchain params # like CXX and CXXFLAGS have been properly set-up by the SDL port build system # before invoking this make file # ifeq ($(origin ANDROID_CXXFLAGS),undefined) $(error Android build can only be invoked from SDL Android port build system. See compile.html for more details) endif # Does platform require symlinks? (it is used to link the openMSX executable # from a location inside the $PATH, which means it is not applicable for # Android platform)) USE_SYMLINK:=false # For Android, a shared library must eventually be build EXEEXT:= LIBRARYEXT:=.so TARGET_FLAGS:=$(ANDROID_LDFLAGS) # Build a maximum set of components. # See configure.py for LINK_MODE definition and usage LINK_MODE:=3RD_STA_GLES openmsx-0.10.0/build/platform-linux.mk0000644000175000017500000000114612262345041020457 0ustar manuelmanuel00000000000000# Configuration for Linux machines. # Does platform support symlinks? USE_SYMLINK:=true # File name extension of executables. EXEEXT:= LIBRARYEXT:=.so # In glibc the function clock_gettime() is defined in the librt library. In # uClibc it's defined in libc itself. We should write a function that actually # tests which library defines it. But as a temporary workaround/hack we figure # out the linker flags based on the occurrence of the substring uclibc inside # the compiler executable name (e.g. for Dingoo we don't need to link against # librt). ifeq (,$(findstring uclibc,$(CXX))) LINK_FLAGS:=-lrt endif openmsx-0.10.0/build/flavour-win32.mk0000644000175000017500000000056712262345041020122 0ustar manuelmanuel00000000000000# Configuration for Win32 flavour: # Optimised for Pentium 3 but runnable at MMX-Pentium or higher. # Optimisation flags. CXXFLAGS+= \ -Os -mpreferred-stack-boundary=4 \ -mtune=pentium3 -march=pentium-mmx -mmmx \ -fstrength-reduce -fexpensive-optimizations -fschedule-insns2 \ -fomit-frame-pointer -fno-default-inline # -DNDEBUG # Strip executable? OPENMSX_STRIP:=true openmsx-0.10.0/build/platform-openbsd.mk0000644000175000017500000000022212262345041020744 0ustar manuelmanuel00000000000000# Configuration for OpenBSD. # Does platform support symlinks? USE_SYMLINK:=true # File name extension of executables. EXEEXT:= LIBRARYEXT:=.so openmsx-0.10.0/build/executils.py0000644000175000017500000000375412262345041017533 0ustar manuelmanuel00000000000000from msysutils import msysActive, msysShell from os import environ from shlex import split as shsplit from subprocess import PIPE, Popen def captureStdout(log, commandLine): '''Run a command and capture what it writes to stdout. If the command fails or writes something to stderr, that is logged. Returns the captured string, or None if the command failed. ''' # TODO: This is a modified copy-paste from compilers._Command. commandParts = shsplit(commandLine) env = dict(environ) while commandParts: if '=' in commandParts[0]: name, value = commandParts[0].split('=', 1) del commandParts[0] env[name] = value else: break else: raise ValueError( 'No command specified in "%s"' % commandLine ) if msysActive() and commandParts[0] != 'sh': commandParts = [ environ.get('MSYSCON') or environ.get('SHELL') or 'sh.exe', '-c', shjoin(commandParts) ] try: proc = Popen( commandParts, bufsize = -1, env = env, stdin = None, stdout = PIPE, stderr = PIPE, ) except OSError, ex: print >> log, 'Failed to execute "%s": %s' % (commandLine, ex) return None stdoutdata, stderrdata = proc.communicate() if stderrdata: severity = 'warning' if proc.returncode == 0 else 'error' log.write('%s executing "%s"\n' % (severity.capitalize(), commandLine)) # pylint 0.18.0 somehow thinks stderrdata is a list, not a string. # pylint: disable-msg=E1103 stderrdata = stderrdata.replace('\r', '') log.write(stderrdata) if not stderrdata.endswith('\n'): log.write('\n') if proc.returncode == 0: return stdoutdata else: print >> log, 'Execution failed with exit code %d' % proc.returncode return None def shjoin(parts): '''Joins the given sequence into a single string with space as a separator. Characters that have a special meaning for the shell are escaped. This is the counterpart of shlex.split(). ''' def escape(part): return ''.join( '\\' + ch if ch in '\\ \'"$()[]' else ch for ch in part ) return ' '.join(escape(part) for part in parts) openmsx-0.10.0/build/list_system_libs.py0000644000175000017500000000074012262345041021106 0ustar manuelmanuel00000000000000# Prints the Make names of those libraries that are present in the base OS. from libraries import librariesByName def main(platform): systemLibs = set( name for name, library in librariesByName.iteritems() if library.isSystemLibrary(platform) ) print ' '.join(sorted(systemLibs)) if __name__ == '__main__': import sys if len(sys.argv) == 2: main(*sys.argv[1 : ]) else: print >> sys.stderr, ( 'Usage: python list_system_libs.py TARGET_OS' ) sys.exit(2) openmsx-0.10.0/build/configurations.py0000644000175000017500000000305612262345041020553 0ustar manuelmanuel00000000000000from components import ( AODriver, EmulationCore, GLRenderer, Laserdisc, iterComponents ) class Configuration(object): def __init__(self, requiredComponents, optionalComponents, linkStatic): self.__requiredComponents = requiredComponents self.__optionalComponents = optionalComponents self.__linkStatic = linkStatic def iterRequiredComponents(self): return iter(self.__requiredComponents) def iterOptionalComponents(self): return iter(self.__optionalComponents) def iterDesiredComponents(self): return iter(self.__requiredComponents | self.__optionalComponents) def linkStatic(self): '''Returns True iff static linking should be used for non-system libs. ''' return self.__linkStatic def getConfiguration(name): if name == 'SYS_DYN': requiredComponents = set((EmulationCore, )) optionalComponents = set(iterComponents()) - requiredComponents linkStatic = False elif name == '3RD_STA': requiredComponents = set((EmulationCore, GLRenderer)) optionalComponents = set(iterComponents()) - requiredComponents linkStatic = True elif name == '3RD_STA_GLES': # TODO: We don't have an OpenGL ES component yet. requiredComponents = set((EmulationCore, )) optionalComponents = \ set(iterComponents()) - requiredComponents - set((GLRenderer, )) linkStatic = True elif name == '3RD_STA_MIN': requiredComponents = set((EmulationCore, )) optionalComponents = set((AODriver, )) linkStatic = True else: raise ValueError('No configuration named "%s"' % name) return Configuration(requiredComponents, optionalComponents, linkStatic) openmsx-0.10.0/build/msvc/0000755000175000017500000000000012262345041016113 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/msvc/openmsx.sln0000644000175000017500000000314412262345041020324 0ustar manuelmanuel00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "openmsx", "openmsx.vcxproj", "{9563D96E-BBDC-410C-8BED-189102B0866F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Developer|Win32 = Developer|Win32 Developer|x64 = Developer|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9563D96E-BBDC-410C-8BED-189102B0866F}.Debug|Win32.ActiveCfg = Debug|Win32 {9563D96E-BBDC-410C-8BED-189102B0866F}.Debug|Win32.Build.0 = Debug|Win32 {9563D96E-BBDC-410C-8BED-189102B0866F}.Debug|x64.ActiveCfg = Debug|x64 {9563D96E-BBDC-410C-8BED-189102B0866F}.Debug|x64.Build.0 = Debug|x64 {9563D96E-BBDC-410C-8BED-189102B0866F}.Developer|Win32.ActiveCfg = Developer|Win32 {9563D96E-BBDC-410C-8BED-189102B0866F}.Developer|Win32.Build.0 = Developer|Win32 {9563D96E-BBDC-410C-8BED-189102B0866F}.Developer|x64.ActiveCfg = Developer|x64 {9563D96E-BBDC-410C-8BED-189102B0866F}.Developer|x64.Build.0 = Developer|x64 {9563D96E-BBDC-410C-8BED-189102B0866F}.Release|Win32.ActiveCfg = Release|Win32 {9563D96E-BBDC-410C-8BED-189102B0866F}.Release|Win32.Build.0 = Release|Win32 {9563D96E-BBDC-410C-8BED-189102B0866F}.Release|x64.ActiveCfg = Release|x64 {9563D96E-BBDC-410C-8BED-189102B0866F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal openmsx-0.10.0/build/msvc/genconfig.py0000644000175000017500000000413712262345041020431 0ustar manuelmanuel00000000000000# Generates configuration headers for VC++ builds import sys import os.path import outpututils import buildinfo2code import components2code import systemfuncs2code import win_resource import version2code # # platform: one of { Win32, x64 } # configuration: one of { Debug, Developer, Release } # outputPath: the location in which to generate config files # def genConfig(platform, configuration, outputPath): buildPath = 'build' msvcPath = os.path.join(buildPath, 'msvc') probeMakePath = os.path.join(msvcPath, 'probed_defs.mk') # # build-info.hh # buildInfoHeader = os.path.join(outputPath, 'build-info.hh') targetPlatform = 'mingw32' if platform == 'Win32': targetCPU = 'x86' elif platform == 'x64': targetCPU = 'x86_64' else: raise ValueError('Invalid platform: ' + platform) flavour = configuration installShareDir = '/opt/openMSX/share' #not used on Windows, so whatever generator = buildinfo2code.iterBuildInfoHeader(targetPlatform, targetCPU, flavour, installShareDir) outpututils.rewriteIfChanged(buildInfoHeader, generator) # # components.hh # componentsHeader = os.path.join(outputPath, 'components.hh') generator = components2code.iterComponentsHeader(probeMakePath) outpututils.rewriteIfChanged(componentsHeader, generator) # # systemfuncs.hh # systemFuncsHeader = os.path.join(outputPath, 'systemfuncs.hh') generator = systemfuncs2code.iterSystemFuncsHeader(systemfuncs2code.getSystemFuncsInfo()) outpututils.rewriteIfChanged(systemFuncsHeader, generator) # # resource-info.hh # resourceInfoHeader = os.path.join(outputPath, 'resource-info.h') generator = win_resource.iterResourceHeader() outpututils.rewriteIfChanged(resourceInfoHeader, generator) # # version.ii # versionHeader = os.path.join(outputPath, 'version.ii') generator = version2code.iterVersionInclude() outpututils.rewriteIfChanged(versionHeader, generator) if len(sys.argv) == 4: genConfig(sys.argv[1], sys.argv[2], sys.argv[3]) else: print >> sys.stderr, 'Usage: python genconfig.py platform configuration outputPath' sys.exit(2) openmsx-0.10.0/build/msvc/node.mk0000644000175000017500000000022212262345041017365 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ genconfig.py openmsx.sln openmsx.vcxproj probed_defs.mk openmsx.vcxproj.filters include build/node-end.mk openmsx-0.10.0/build/msvc/probed_defs.mk0000644000175000017500000000114712262345041020723 0ustar manuelmanuel00000000000000# Hardcoded probe results for Visual C++ build. # Non-empty value means found, empty means not found. HAVE_GL_H:=true HAVE_GL_LIB:=true HAVE_GLEW_H:=true HAVE_GLEW_LIB:=true HAVE_LASERDISC_H:=true HAVE_LASERDISC_LIB:=true HAVE_OGG_H:=true HAVE_OGG_LIB=true HAVE_PNG_H:=true HAVE_PNG_LIB:=true HAVE_SDL_H:=true HAVE_SDL_LIB:=true HAVE_SDL_IMAGE_H:=true HAVE_SDL_IMAGE_LIB:=true HAVE_SDL_TTF_H:=true HAVE_SDL_TTF_LIB:=true HAVE_TCL_H:=true HAVE_TCL_LIB:=true HAVE_THEORA_H:=true HAVE_THEORA_LIB:=true HAVE_VORBIS_H:=true HAVE_VORBIS_LIB:=true HAVE_XML_H:=true HAVE_XML_LIB:=true HAVE_ZLIB_H:=true HAVE_ZLIB_LIB:=true openmsx-0.10.0/build/msvc/openmsx.vcxproj0000644000175000017500000031305312262345041021226 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {9563D96E-BBDC-410C-8BED-189102B0866F} openmsx Win32Proj Application Unicode Application Unicode true Application Unicode Application Unicode Application Unicode true Application Unicode <_ProjectFileVersion>10.0.30319.1 $(OpenMSXOutDir)\ $(OpenMSXIntDir)\ $(OpenMSXOutDir)\ $(OpenMSXIntDir)\ $(OpenMSXOutDir)\ $(OpenMSXIntDir)\ $(OpenMSXOutDir)\ $(OpenMSXIntDir)\ $(OpenMSXOutDir)\ $(OpenMSXIntDir)\ $(OpenMSXOutDir)\ $(OpenMSXIntDir)\ /MP %(AdditionalOptions) Disabled $(OpenMSXConfigDir);$(DXSDK_DIR)\Include;$(ThirdPartySrcDir)\$(LibNameFreeType);$(ThirdPartySrcDir)\$(LibNameGlew)\include;$(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameLibXml)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameSDL_ttf);$(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameZlib);$(OpenMSXSrcDir);$(OpenMSXSrcDir)\cassette;$(OpenMSXSrcDir)\commands;$(OpenMSXSrcDir)\config;$(OpenMSXSrcDir)\console;$(OpenMSXSrcDir)\cpu;$(OpenMSXSrcDir)\debugger;$(OpenMSXSrcDir)\events;$(OpenMSXSrcDir)\fdc;$(OpenMSXSrcDir)\file;$(OpenMSXSrcDir)\ide;$(OpenMSXSrcDir)\input;$(OpenMSXSrcDir)\laserdisc;$(OpenMSXSrcDir)\memory;$(OpenMSXSrcDir)\resource;$(OpenMSXSrcDir)\security;$(OpenMSXSrcDir)\serial;$(OpenMSXSrcDir)\settings;$(OpenMSXSrcDir)\sound;$(OpenMSXSrcDir)\thread;$(OpenMSXSrcDir)\utils;$(OpenMSXSrcDir)\video;$(OpenMSXSrcDir)\video\scalers;$(OpenMSXSrcDir)\video\ld;$(OpenMSXSrcDir)\video\v9990;%(AdditionalIncludeDirectories) __SSE2__;WIN32;WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;SECURITY_WIN32;DEBUG;_DEBUG;_CONSOLE;_USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;NOMINMAX;LIBXML_STATIC;GLEW_STATIC;STATIC_BUILD;%(PreprocessorDefinitions) MultiThreadedDebug Level4 ProgramDatabase 4324;4063;4121;4125;4127;4189;4201;4244;4310;4355;4505;4512;4611;4702;%(DisableSpecificWarnings) $(OpenMSXConfigDir);$(ThirdPartySrcDir)\$(LibNameTcl)\generic;%(AdditionalIncludeDirectories) wsock32.lib;winmm.lib;secur32.lib;opengl32.lib;dxguid.lib;dsound.lib;SDL.lib;SDLmain.lib;SDL_ttf.lib;freetype.lib;glew.lib;libogg.lib;libpng.lib;libtheora.lib;libvorbis.lib;libxml.lib;tcl.lib;zlib.lib;%(AdditionalDependencies) $(OutDir)openmsx.exe $(DXSDK_DIR)\Lib\$(PlatformShortName);$(ThirdPartyOutDir);%(AdditionalLibraryDirectories) %(IgnoreSpecificDefaultLibraries) shell32.dll;%(DelayLoadDLLs) true Console MachineX86 /SAFESEH:NO %(AdditionalOptions) X64 /MP %(AdditionalOptions) Disabled $(OpenMSXConfigDir);$(DXSDK_DIR)\Include;$(ThirdPartySrcDir)\$(LibNameFreeType);$(ThirdPartySrcDir)\$(LibNameGlew)\include;$(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameLibXml)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameSDL_ttf);$(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameZlib);$(OpenMSXSrcDir);$(OpenMSXSrcDir)\cassette;$(OpenMSXSrcDir)\commands;$(OpenMSXSrcDir)\config;$(OpenMSXSrcDir)\console;$(OpenMSXSrcDir)\cpu;$(OpenMSXSrcDir)\debugger;$(OpenMSXSrcDir)\events;$(OpenMSXSrcDir)\fdc;$(OpenMSXSrcDir)\file;$(OpenMSXSrcDir)\ide;$(OpenMSXSrcDir)\input;$(OpenMSXSrcDir)\laserdisc;$(OpenMSXSrcDir)\memory;$(OpenMSXSrcDir)\resource;$(OpenMSXSrcDir)\security;$(OpenMSXSrcDir)\serial;$(OpenMSXSrcDir)\settings;$(OpenMSXSrcDir)\sound;$(OpenMSXSrcDir)\thread;$(OpenMSXSrcDir)\utils;$(OpenMSXSrcDir)\video;$(OpenMSXSrcDir)\video\scalers;$(OpenMSXSrcDir)\video\ld;$(OpenMSXSrcDir)\video\v9990;%(AdditionalIncludeDirectories) __SSE2__;WIN32;_WIN64;__x86_64;UNICODE;_UNICODE;WIN32_LEAN_AND_MEAN;SECURITY_WIN32;DEBUG;_DEBUG;_CONSOLE;_USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;NOMINMAX;LIBXML_STATIC;GLEW_STATIC;STATIC_BUILD;%(PreprocessorDefinitions) MultiThreadedDebug Level4 ProgramDatabase 4324;4063;4121;4125;4127;4189;4201;4244;4310;4355;4505;4512;4611;4702;%(DisableSpecificWarnings) $(OpenMSXConfigDir);$(ThirdPartySrcDir)\$(LibNameTcl)\generic;%(AdditionalIncludeDirectories) wsock32.lib;winmm.lib;secur32.lib;opengl32.lib;dxguid.lib;dsound.lib;SDL.lib;SDLmain.lib;SDL_ttf.lib;freetype.lib;glew.lib;libogg.lib;libpng.lib;libtheora.lib;libvorbis.lib;libxml.lib;tcl.lib;zlib.lib;%(AdditionalDependencies) $(OutDir)openmsx.exe $(DXSDK_DIR)\Lib\$(Platform);$(ThirdPartyOutDir);%(AdditionalLibraryDirectories) shell32.dll;%(DelayLoadDLLs) true Console MachineX64 /MP %(AdditionalOptions) Full AnySuitable true Size true true true $(OpenMSXConfigDir);$(DXSDK_DIR)\Include;$(ThirdPartySrcDir)\$(LibNameFreeType);$(ThirdPartySrcDir)\$(LibNameGlew)\include;$(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameLibXml)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameSDL_ttf);$(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameZlib);$(OpenMSXSrcDir);$(OpenMSXSrcDir)\cassette;$(OpenMSXSrcDir)\commands;$(OpenMSXSrcDir)\config;$(OpenMSXSrcDir)\console;$(OpenMSXSrcDir)\cpu;$(OpenMSXSrcDir)\debugger;$(OpenMSXSrcDir)\events;$(OpenMSXSrcDir)\fdc;$(OpenMSXSrcDir)\file;$(OpenMSXSrcDir)\ide;$(OpenMSXSrcDir)\input;$(OpenMSXSrcDir)\laserdisc;$(OpenMSXSrcDir)\memory;$(OpenMSXSrcDir)\resource;$(OpenMSXSrcDir)\security;$(OpenMSXSrcDir)\serial;$(OpenMSXSrcDir)\settings;$(OpenMSXSrcDir)\sound;$(OpenMSXSrcDir)\thread;$(OpenMSXSrcDir)\utils;$(OpenMSXSrcDir)\video;$(OpenMSXSrcDir)\video\scalers;$(OpenMSXSrcDir)\video\ld;$(OpenMSXSrcDir)\video\v9990;%(AdditionalIncludeDirectories) __SSE2__;WIN32;WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;SECURITY_WIN32;NDEBUG;_CONSOLE;_USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;NOMINMAX;LIBXML_STATIC;GLEW_STATIC;STATIC_BUILD;%(PreprocessorDefinitions) true false MultiThreaded true Level4 ProgramDatabase 4324;4063;4121;4125;4127;4189;4201;4244;4310;4355;4505;4512;4611;4702;%(DisableSpecificWarnings) $(OpenMSXConfigDir);$(ThirdPartySrcDir)\$(LibNameTcl)\generic;%(AdditionalIncludeDirectories) wsock32.lib;winmm.lib;secur32.lib;opengl32.lib;dxguid.lib;dsound.lib;SDL.lib;SDLmain.lib;SDL_ttf.lib;freetype.lib;glew.lib;libogg.lib;libpng.lib;libtheora.lib;libvorbis.lib;libxml.lib;tcl.lib;zlib.lib;%(AdditionalDependencies) $(OutDir)openmsx.exe $(DXSDK_DIR)\Lib\$(PlatformShortName);$(ThirdPartyOutDir);%(AdditionalLibraryDirectories) shell32.dll;%(DelayLoadDLLs) true Console true true MachineX86 /SAFESEH:NO %(AdditionalOptions) X64 /MP %(AdditionalOptions) Full AnySuitable true Size true true $(OpenMSXConfigDir);$(DXSDK_DIR)\Include;$(ThirdPartySrcDir)\$(LibNameFreeType);$(ThirdPartySrcDir)\$(LibNameGlew)\include;$(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameLibXml)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameSDL_ttf);$(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameZlib);$(OpenMSXSrcDir);$(OpenMSXSrcDir)\cassette;$(OpenMSXSrcDir)\commands;$(OpenMSXSrcDir)\config;$(OpenMSXSrcDir)\console;$(OpenMSXSrcDir)\cpu;$(OpenMSXSrcDir)\debugger;$(OpenMSXSrcDir)\events;$(OpenMSXSrcDir)\fdc;$(OpenMSXSrcDir)\file;$(OpenMSXSrcDir)\ide;$(OpenMSXSrcDir)\input;$(OpenMSXSrcDir)\laserdisc;$(OpenMSXSrcDir)\memory;$(OpenMSXSrcDir)\resource;$(OpenMSXSrcDir)\security;$(OpenMSXSrcDir)\serial;$(OpenMSXSrcDir)\settings;$(OpenMSXSrcDir)\sound;$(OpenMSXSrcDir)\thread;$(OpenMSXSrcDir)\utils;$(OpenMSXSrcDir)\video;$(OpenMSXSrcDir)\video\scalers;$(OpenMSXSrcDir)\video\ld;$(OpenMSXSrcDir)\video\v9990;%(AdditionalIncludeDirectories) __SSE2__;WIN32;_WIN64;__x86_64;WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;SECURITY_WIN32;NDEBUG;_CONSOLE;_USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;NOMINMAX;LIBXML_STATIC;GLEW_STATIC;STATIC_BUILD;%(PreprocessorDefinitions) true MultiThreaded true Level4 ProgramDatabase 4324;4063;4121;4125;4127;4189;4201;4244;4310;4355;4505;4512;4611;4702;%(DisableSpecificWarnings) $(OpenMSXConfigDir);$(ThirdPartySrcDir)\$(LibNameTcl)\generic;%(AdditionalIncludeDirectories) wsock32.lib;winmm.lib;secur32.lib;opengl32.lib;dxguid.lib;dsound.lib;SDL.lib;SDLmain.lib;SDL_ttf.lib;freetype.lib;glew.lib;libogg.lib;libpng.lib;libtheora.lib;libvorbis.lib;libxml.lib;tcl.lib;zlib.lib;%(AdditionalDependencies) $(OutDir)openmsx.exe $(DXSDK_DIR)\Lib\$(Platform);$(ThirdPartyOutDir);%(AdditionalLibraryDirectories) shell32.dll;%(DelayLoadDLLs) true Console true true MachineX64 /MP %(AdditionalOptions) Disabled $(OpenMSXConfigDir);$(DXSDK_DIR)\Include;$(ThirdPartySrcDir)\$(LibNameFreeType);$(ThirdPartySrcDir)\$(LibNameGlew)\include;$(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameLibXml)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameSDL_ttf);$(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameZlib);$(OpenMSXSrcDir);$(OpenMSXSrcDir)\cassette;$(OpenMSXSrcDir)\commands;$(OpenMSXSrcDir)\config;$(OpenMSXSrcDir)\console;$(OpenMSXSrcDir)\cpu;$(OpenMSXSrcDir)\debugger;$(OpenMSXSrcDir)\events;$(OpenMSXSrcDir)\fdc;$(OpenMSXSrcDir)\file;$(OpenMSXSrcDir)\ide;$(OpenMSXSrcDir)\input;$(OpenMSXSrcDir)\laserdisc;$(OpenMSXSrcDir)\memory;$(OpenMSXSrcDir)\resource;$(OpenMSXSrcDir)\security;$(OpenMSXSrcDir)\serial;$(OpenMSXSrcDir)\settings;$(OpenMSXSrcDir)\sound;$(OpenMSXSrcDir)\thread;$(OpenMSXSrcDir)\utils;$(OpenMSXSrcDir)\video;$(OpenMSXSrcDir)\video\scalers;$(OpenMSXSrcDir)\video\ld;$(OpenMSXSrcDir)\video\v9990;%(AdditionalIncludeDirectories) __SSE2__;WIN32;WIN32_LEAN_AND_MEAN;UNICODE;_UNICODE;SECURITY_WIN32;_DEBUG;_CONSOLE;_USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;NOMINMAX;LIBXML_STATIC;GLEW_STATIC;STATIC_BUILD;%(PreprocessorDefinitions) true MultiThreadedDebug Level4 ProgramDatabase 4324;4063;4121;4125;4127;4189;4201;4244;4310;4355;4505;4512;4611;4702;%(DisableSpecificWarnings) $(OpenMSXConfigDir);$(ThirdPartySrcDir)\$(LibNameTcl)\generic;%(AdditionalIncludeDirectories) wsock32.lib;winmm.lib;secur32.lib;opengl32.lib;dxguid.lib;dsound.lib;SDL.lib;SDLmain.lib;SDL_ttf.lib;freetype.lib;glew.lib;libogg.lib;libpng.lib;libtheora.lib;libvorbis.lib;libxml.lib;tcl.lib;zlib.lib;%(AdditionalDependencies) $(OutDir)openmsx.exe $(DXSDK_DIR)\Lib\$(PlatformShortName);$(ThirdPartyOutDir);%(AdditionalLibraryDirectories) %(IgnoreSpecificDefaultLibraries) shell32.dll;%(DelayLoadDLLs) true Console MachineX86 /SAFESEH:NO %(AdditionalOptions) X64 /MP %(AdditionalOptions) Disabled $(OpenMSXConfigDir);$(DXSDK_DIR)\Include;$(ThirdPartySrcDir)\$(LibNameFreeType);$(ThirdPartySrcDir)\$(LibNameGlew)\include;$(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameLibXml)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameSDL_ttf);$(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameZlib);$(OpenMSXSrcDir);$(OpenMSXSrcDir)\cassette;$(OpenMSXSrcDir)\commands;$(OpenMSXSrcDir)\config;$(OpenMSXSrcDir)\console;$(OpenMSXSrcDir)\cpu;$(OpenMSXSrcDir)\debugger;$(OpenMSXSrcDir)\events;$(OpenMSXSrcDir)\fdc;$(OpenMSXSrcDir)\file;$(OpenMSXSrcDir)\ide;$(OpenMSXSrcDir)\input;$(OpenMSXSrcDir)\laserdisc;$(OpenMSXSrcDir)\memory;$(OpenMSXSrcDir)\resource;$(OpenMSXSrcDir)\security;$(OpenMSXSrcDir)\serial;$(OpenMSXSrcDir)\settings;$(OpenMSXSrcDir)\sound;$(OpenMSXSrcDir)\thread;$(OpenMSXSrcDir)\utils;$(OpenMSXSrcDir)\video;$(OpenMSXSrcDir)\video\scalers;$(OpenMSXSrcDir)\video\ld;$(OpenMSXSrcDir)\video\v9990;%(AdditionalIncludeDirectories) __SSE2__;WIN32;_WIN64;__x86_64;UNICODE;_UNICODE;WIN32_LEAN_AND_MEAN;SECURITY_WIN32;_DEBUG;_CONSOLE;_USE_MATH_DEFINES;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;NOMINMAX;LIBXML_STATIC;GLEW_STATIC;STATIC_BUILD;%(PreprocessorDefinitions) true MultiThreadedDebug Level4 ProgramDatabase 4324;4063;4121;4125;4127;4189;4201;4244;4310;4355;4505;4512;4611;4702;%(DisableSpecificWarnings) $(OpenMSXConfigDir);$(ThirdPartySrcDir)\$(LibNameTcl)\generic;%(AdditionalIncludeDirectories) wsock32.lib;winmm.lib;secur32.lib;opengl32.lib;dxguid.lib;dsound.lib;SDL.lib;SDLmain.lib;SDL_ttf.lib;freetype.lib;glew.lib;libogg.lib;libpng.lib;libtheora.lib;libvorbis.lib;libxml.lib;tcl.lib;zlib.lib;%(AdditionalDependencies) $(OutDir)openmsx.exe $(DXSDK_DIR)\Lib\$(Platform);$(ThirdPartyOutDir);%(AdditionalLibraryDirectories) shell32.dll;%(DelayLoadDLLs) true Console MachineX64 Document Document Document Document Document Document Document Document Document Document Generating config headers... for /f "delims=" %%d in ("$(OpenMSXConfigDir)") do set CONFIG_DIR=%%~fd rem echo CONFIG_DIR=%CONFIG_DIR% for /f "delims=" %%d in ("$(OpenMSXRootDir)") do set ROOT_DIR=%%~fd rem echo ROOT_DIR=%ROOT_DIR% set BUILD_DIR=%ROOT_DIR%\build rem echo BUILD_DIR=%BUILD_DIR% set MSVC_DIR=%BUILD_DIR%\msvc rem echo MSVC_DIR=%MSVC_DIR% set PYTHONPATH=%PYTHONPATH%;%BUILD_DIR% rem echo PYTHONPATH=%PYTHONPATH% pushd %ROOT_DIR% python "%MSVC_DIR%\genconfig.py" $(Platform) "$(Configuration)" "%CONFIG_DIR%" popd build-info.hh;components.hh;probed_defs.hh;resource-info.h;Version.ii;%(Outputs) Generating config headers... for /f "delims=" %%d in ("$(OpenMSXConfigDir)") do set CONFIG_DIR=%%~fd rem echo CONFIG_DIR=%CONFIG_DIR% for /f "delims=" %%d in ("$(OpenMSXRootDir)") do set ROOT_DIR=%%~fd rem echo ROOT_DIR=%ROOT_DIR% set BUILD_DIR=%ROOT_DIR%\build rem echo BUILD_DIR=%BUILD_DIR% set MSVC_DIR=%BUILD_DIR%\msvc rem echo MSVC_DIR=%MSVC_DIR% set PYTHONPATH=%PYTHONPATH%;%BUILD_DIR% rem echo PYTHONPATH=%PYTHONPATH% pushd %ROOT_DIR% python "%MSVC_DIR%\genconfig.py" $(Platform) "$(Configuration)" "%CONFIG_DIR%" popd build-info.hh;components.hh;probed_defs.hh;resource-info.h;Version.ii;%(Outputs) Generating config headers... for /f "delims=" %%d in ("$(OpenMSXConfigDir)") do set CONFIG_DIR=%%~fd rem echo CONFIG_DIR=%CONFIG_DIR% for /f "delims=" %%d in ("$(OpenMSXRootDir)") do set ROOT_DIR=%%~fd rem echo ROOT_DIR=%ROOT_DIR% set BUILD_DIR=%ROOT_DIR%\build rem echo BUILD_DIR=%BUILD_DIR% set MSVC_DIR=%BUILD_DIR%\msvc rem echo MSVC_DIR=%MSVC_DIR% set PYTHONPATH=%PYTHONPATH%;%BUILD_DIR% rem echo PYTHONPATH=%PYTHONPATH% pushd %ROOT_DIR% python "%MSVC_DIR%\genconfig.py" $(Platform) "$(Configuration)" "%CONFIG_DIR%" popd build-info.hh;components.hh;probed_defs.hh;resource-info.h;Version.ii;%(Outputs) Generating config headers... for /f "delims=" %%d in ("$(OpenMSXConfigDir)") do set CONFIG_DIR=%%~fd rem echo CONFIG_DIR=%CONFIG_DIR% for /f "delims=" %%d in ("$(OpenMSXRootDir)") do set ROOT_DIR=%%~fd rem echo ROOT_DIR=%ROOT_DIR% set BUILD_DIR=%ROOT_DIR%\build rem echo BUILD_DIR=%BUILD_DIR% set MSVC_DIR=%BUILD_DIR%\msvc rem echo MSVC_DIR=%MSVC_DIR% set PYTHONPATH=%PYTHONPATH%;%BUILD_DIR% rem echo PYTHONPATH=%PYTHONPATH% pushd %ROOT_DIR% python "%MSVC_DIR%\genconfig.py" $(Platform) "$(Configuration)" "%CONFIG_DIR%" popd build-info.hh;components.hh;probed_defs.hh;resource-info.h;Version.ii;%(Outputs) Generating config headers... for /f "delims=" %%d in ("$(OpenMSXConfigDir)") do set CONFIG_DIR=%%~fd rem echo CONFIG_DIR=%CONFIG_DIR% for /f "delims=" %%d in ("$(OpenMSXRootDir)") do set ROOT_DIR=%%~fd rem echo ROOT_DIR=%ROOT_DIR% set BUILD_DIR=%ROOT_DIR%\build rem echo BUILD_DIR=%BUILD_DIR% set MSVC_DIR=%BUILD_DIR%\msvc rem echo MSVC_DIR=%MSVC_DIR% set PYTHONPATH=%PYTHONPATH%;%BUILD_DIR% rem echo PYTHONPATH=%PYTHONPATH% pushd %ROOT_DIR% python "%MSVC_DIR%\genconfig.py" $(Platform) "$(Configuration)" "%CONFIG_DIR%" popd build-info.hh;components.hh;probed_defs.hh;resource-info.h;Version.ii;%(Outputs) Generating config headers... for /f "delims=" %%d in ("$(OpenMSXConfigDir)") do set CONFIG_DIR=%%~fd rem echo CONFIG_DIR=%CONFIG_DIR% for /f "delims=" %%d in ("$(OpenMSXRootDir)") do set ROOT_DIR=%%~fd rem echo ROOT_DIR=%ROOT_DIR% set BUILD_DIR=%ROOT_DIR%\build rem echo BUILD_DIR=%BUILD_DIR% set MSVC_DIR=%BUILD_DIR%\msvc rem echo MSVC_DIR=%MSVC_DIR% set PYTHONPATH=%PYTHONPATH%;%BUILD_DIR% rem echo PYTHONPATH=%PYTHONPATH% pushd %ROOT_DIR% python "%MSVC_DIR%\genconfig.py" $(Platform) "$(Configuration)" "%CONFIG_DIR%" popd build-info.hh;components.hh;probed_defs.hh;resource-info.h;Version.ii;%(Outputs) openmsx-0.10.0/build/msvc/openmsx.vcxproj.filters0000644000175000017500000033036012262345041022675 0ustar manuelmanuel00000000000000 {27eaf1e2-f5da-477a-99a5-7be15006d6a0} {878501bc-d7f0-49ec-bc35-d2e22835aaf6} {a5140952-ef4a-4d86-b93f-8562cf838f1f} {2bd3c002-6e7b-444b-a02f-75e6e57aa28e} {99b1a5c3-9248-4127-bb7a-bffc6052fa1d} {2f1531a3-2bff-4bb2-b737-8b5bc263a5d0} {4b659948-74af-4e70-9469-71cf9e8ff90e} {0d4a99d7-3f0c-4bd7-94c9-73f2ad724887} {a36193d1-ca1d-4188-aefc-60d10a41573b} {db111771-419a-4e9f-a046-87a88f567ce5} {39b914e9-534c-42eb-8b1c-7078665cd464} {e010ea5f-02d9-420a-af08-577f6fac9b71} {4790c7a8-2904-4fda-9248-fc5357d9751f} {6bf182df-f9bf-43c3-9d2a-c0984b19716c} {234f2a7b-0398-45c2-9149-a918a0d564bd} {c12b54ed-4653-4ee0-b86a-e5c90a8b9b8d} {5db6c172-d415-41b3-b89d-876e94c600d7} {f34953a9-8bb9-4fe7-9d92-38dd046b936a} {dc7744b4-c613-43ff-8e2b-d89fb8b90f2b} {441fef5c-d4c3-4cb1-8bd9-27253f5a929a} {8fe6dae8-765e-428c-abf4-662e83a99385} {ce8126f9-f849-4f95-85bf-f6919a3a4e9a} {3b3e10a3-7923-46c0-b78f-46acd840d7c2} cassette cassette cassette cassette cassette cassette cassette cassette commands commands commands commands commands commands commands commands commands commands config config config config console console console console console console console console console console cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu debugger debugger debugger debugger debugger events events events events events events events events events events events events events events fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc file file file file file file file file file file file file file file ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide input input input input input input input input input input input input input input input input input input input memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory settings settings settings settings settings settings settings settings settings settings settings settings sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound thread thread thread thread thread thread utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\ld video\ld video\ld video\ld serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial security security security laserdisc laserdisc laserdisc laserdisc laserdisc memory cassette cassette cassette cassette cassette cassette cassette cassette commands commands commands commands commands commands commands commands commands commands commands commands config config config config config config console console console console console console console console console console cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu cpu debugger debugger debugger debugger debugger debugger events events events events events events events events events events events events events events events events fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc fdc file file file file file file file file file file file file file file file ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide ide input input input input input input input input input input input input input input input input input input input input input input memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory memory resource settings settings settings settings settings settings settings settings settings settings settings settings settings settings settings sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound sound thread thread thread thread thread thread utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils utils video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 video\v9990 serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial serial security security security memory resource sound video\ld video\ld video\ld video\ld video\ld laserdisc laserdisc laserdisc laserdisc laserdisc openmsx-0.10.0/build/flavour-opt.mk0000644000175000017500000000061312262345041017752 0ustar manuelmanuel00000000000000# Generic optimisation flavour: # does not target any specific CPU. # Optimisation flags. CXXFLAGS+=-O3 -DNDEBUG \ -ffast-math -funroll-loops # openMSX crashes when the win32 version is built with -fomit-frame-pointer # TODO: investigate and recheck when new compiler is used. ifneq ($(OPENMSX_TARGET_OS),mingw32) CXXFLAGS+=-fomit-frame-pointer endif # Strip executable? OPENMSX_STRIP:=true openmsx-0.10.0/build/flavour-super-opt.mk0000644000175000017500000000216712262345041021114 0ustar manuelmanuel00000000000000# This flavour enables some extra optimizations, though these options may # not work everywhere or may not be benefical in all cases. # Start with generic optimisation flags. include build/flavour-opt.mk # Add CPU specific optimization flags: # march=native is only supported starting from gcc-4.2.x # comment out this line if you're compiling on an older gcc version CXXFLAGS+=-march=native -mtune=native # Use computed goto's to speedup Z80 emulation: # - Computed goto's are a gcc extension, it's not part of the official c++ # standard. So this will only work if you use gcc as your compiler (it # won't work with visual c++ for example) # - This is only beneficial on CPUs with branch prediction for indirect jumps # and a reasonable amout of cache. For example it is very benefical for a # intel core2 cpu (10% faster), but not for a ARM920 (a few percent slower) # - Compiling src/cpu/CPUCore.cc with computed goto's enabled is very demanding # on the compiler. On older gcc versions it requires upto 1.5GB of memory. # But even on more recent gcc versions it still requires around 700MB. CXXFLAGS+=-DUSE_COMPUTED_GOTO openmsx-0.10.0/build/libraries.py0000644000175000017500000004625112262345041017501 0ustar manuelmanuel00000000000000# Some notes about static linking: # There are two ways of linking to static library: using the -l command line # option or specifying the full path to the library file as one of the inputs. # When using the -l option, the library search paths will be searched for a # dynamic version of the library, if that is not found, the search paths will # be searched for a static version of the library. This means we cannot force # static linking of a library this way. It is possible to force static linking # of all libraries, but we want to control it per library. # Conclusion: We have to specify the full path to each library that should be # linked statically. from executils import captureStdout, shjoin from os import listdir from os.path import isdir, isfile from os import environ class Library(object): libName = None makeName = None header = None configScriptName = None dynamicLibsOption = '--libs' staticLibsOption = None function = None # TODO: A library can give an application compile time and run time # dependencies on other libraries. For example SDL_ttf depends on # FreeType only at run time, but depends on SDL both compile time # and run time, since SDL is part of its interface and FreeType is # only used by the implementation. As a result, it is possible to # compile against SDL_ttf without having the FreeType headers # installed. But our getCompileFlags() does not support this. # In pkg-config these are called private dependencies. dependsOn = () @classmethod def isSystemLibrary(cls, platform): # pylint: disable-msg=W0613 '''Returns True iff this library is a system library on the given platform. A system library is a library that is available systemwide in the minimal installation of the OS. The default implementation returns False. ''' return False @classmethod def getConfigScript( # pylint: disable-msg=W0613 cls, platform, linkStatic, distroRoot ): scriptName = cls.configScriptName if scriptName is None: return None elif platform == 'dingux' and cls.isSystemLibrary(platform): # TODO: A generic mechanism for locating config scripts in SDKs. # Note that distroRoot is for non-system libs only. # Trying a path relative to the compiler location would # probably work well. return '/opt/a320-toolchain/usr/mipsel-a320-linux-uclibc/sysroot/usr/bin/%s' % scriptName elif distroRoot is None: return scriptName else: return '%s/bin/%s' % (distroRoot, scriptName) @classmethod def getHeaders(cls, platform): # pylint: disable-msg=W0613 header = cls.header return header if hasattr(header, '__iter__') else (header, ) @classmethod def getLibName(cls, platform): # pylint: disable-msg=W0613 return cls.libName @classmethod def getCompileFlags(cls, platform, linkStatic, distroRoot): if platform == 'android': return environ['ANDROID_CXXFLAGS'] configScript = cls.getConfigScript(platform, linkStatic, distroRoot) if configScript is not None: flags = [ '`%s --cflags`' % configScript ] elif distroRoot is None or cls.isSystemLibrary(platform): flags = [] else: flags = [ '-I%s/include' % distroRoot ] dependentFlags = [ librariesByName[name].getCompileFlags( platform, linkStatic, distroRoot ) for name in cls.dependsOn ] return ' '.join(flags + dependentFlags) @classmethod def getLinkFlags(cls, platform, linkStatic, distroRoot): if platform == 'android': return environ['ANDROID_LDFLAGS'] configScript = cls.getConfigScript(platform, linkStatic, distroRoot) if configScript is not None: libsOption = ( cls.dynamicLibsOption if not linkStatic or cls.isSystemLibrary(platform) else cls.staticLibsOption ) if libsOption is not None: return '`%s %s`' % (configScript, libsOption) if distroRoot is None or cls.isSystemLibrary(platform): return '-l%s' % cls.getLibName(platform) else: flags = [ '%s/lib/lib%s.a' % (distroRoot, cls.getLibName(platform)) ] if linkStatic else [ '-L%s/lib -l%s' % (distroRoot, cls.getLibName(platform)) ] dependentFlags = [ librariesByName[name].getLinkFlags( platform, linkStatic, distroRoot ) for name in cls.dependsOn ] systemDependentFlags = list(cls.getSystemDependentFlags(platform)) return ' '.join(flags + dependentFlags + systemDependentFlags) @classmethod def getSystemDependentFlags(cls, platform): return () @classmethod def getVersion(cls, platform, linkStatic, distroRoot): '''Returns the version of this library, "unknown" if there is no mechanism to retrieve the version, None if there is a mechanism to retrieve the version but it failed, or a callable that takes a CompileCommand and a log stream as its arguments and returns the version or None if retrieval failed. ''' configScript = cls.getConfigScript(platform, linkStatic, distroRoot) if configScript is None: return 'unknown' else: return '`%s --version`' % configScript class Expat(Library): libName = 'expat' makeName = 'EXPAT' header = '' function = 'XML_ParserCreate' @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') @classmethod def getVersion(cls, platform, linkStatic, distroRoot): def execute(cmd, log): versionTuple = cmd.expand( log, cls.getHeaders(platform), 'XML_MAJOR_VERSION', 'XML_MINOR_VERSION', 'XML_MICRO_VERSION' ) return None if versionTuple is None else '.'.join(versionTuple) return execute class FreeType(Library): libName = 'freetype' makeName = 'FREETYPE' header = ('', 'FT_FREETYPE_H') configScriptName = 'freetype-config' function = 'FT_Open_Face' @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') @classmethod def getConfigScript(cls, platform, linkStatic, distroRoot): if platform in ('netbsd', 'openbsd'): if distroRoot == '/usr/local': # FreeType is located in the X11 tree, not the ports tree. distroRoot = '/usr/X11R6' return super(FreeType, cls).getConfigScript( platform, linkStatic, distroRoot ) @classmethod def getVersion(cls, platform, linkStatic, distroRoot): configScript = cls.getConfigScript(platform, linkStatic, distroRoot) return '`%s --ftversion`' % configScript class GL(Library): libName = 'GL' makeName = 'GL' function = 'glGenTextures' @classmethod def isSystemLibrary(cls, platform): # On *BSD, OpenGL is in ports, not in the base system. return not platform.endswith('bsd') @classmethod def getHeaders(cls, platform): if platform == 'darwin': return ('', ) else: return ('', ) @classmethod def getCompileFlags(cls, platform, linkStatic, distroRoot): if platform in ('netbsd', 'openbsd'): return '-I/usr/X11R6/include -I/usr/X11R7/include' else: return super(GL, cls).getCompileFlags( platform, linkStatic, distroRoot ) @classmethod def getLinkFlags(cls, platform, linkStatic, distroRoot): if platform == 'darwin': return '-framework OpenGL' elif platform == 'mingw32': return '-lopengl32' elif platform in ('netbsd', 'openbsd'): return '-L/usr/X11R6/lib -L/usr/X11R7/lib -lGL' else: return super(GL, cls).getLinkFlags(platform, linkStatic, distroRoot) @classmethod def getVersion(cls, platform, linkStatic, distroRoot): def execute(cmd, log): versionPairs = tuple( ( major, minor ) for major in range(1, 10) for minor in range(0, 10) ) version = cmd.expand(log, cls.getHeaders(platform), *( 'GL_VERSION_%d_%d' % pair for pair in versionPairs )) try: return '%d.%d' % max( ver for ver, exp in zip(versionPairs, version) if exp is not None ) except ValueError: return None return execute class GLEW(Library): makeName = 'GLEW' header = '' function = 'glewInit' dependsOn = ('GL', ) @classmethod def getLibName(cls, platform): if platform == 'mingw32': return 'glew32' else: return 'GLEW' @classmethod def getCompileFlags(cls, platform, linkStatic, distroRoot): flags = super(GLEW, cls).getCompileFlags( platform, linkStatic, distroRoot ) if platform == 'mingw32' and linkStatic: return '%s -DGLEW_STATIC' % flags else: return flags class LibAO(Library): libName = 'ao' makeName = 'AO' header = '' function = 'ao_open_live' @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') @classmethod def getSystemDependentFlags(cls, platform): if platform in ('linux', 'dingux'): return ('-ldl', ) elif platform == 'mingw32': return ('-lwinmm', ) else: return () class LibPNG(Library): libName = 'png12' makeName = 'PNG' header = '' configScriptName = 'libpng-config' dynamicLibsOption = '--ldflags' function = 'png_write_image' dependsOn = ('ZLIB', ) @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') class LibXML2(Library): libName = 'xml2' makeName = 'XML' header = '' configScriptName = 'xml2-config' function = 'xmlParseDocument' dependsOn = ('ZLIB', ) @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'darwin') @classmethod def getConfigScript(cls, platform, linkStatic, distroRoot): if platform == 'darwin': # Use xml2-config from /usr: ideally we would use xml2-config from # the SDK, but the SDK doesn't contain that file. The -isysroot # compiler argument makes sure the headers are taken from the SDK # though. return '/usr/bin/%s' % cls.configScriptName else: return super(LibXML2, cls).getConfigScript( platform, linkStatic, distroRoot ) @classmethod def getCompileFlags(cls, platform, linkStatic, distroRoot): flags = super(LibXML2, cls).getCompileFlags( platform, linkStatic, distroRoot ) if not linkStatic or cls.isSystemLibrary(platform): return flags else: return flags + ' -DLIBXML_STATIC' class OGG(Library): libName = 'ogg' makeName = 'OGG' header = '' function = 'ogg_stream_init' @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') class SDL(Library): libName = 'SDL' makeName = 'SDL' header = '' configScriptName = 'sdl-config' staticLibsOption = '--static-libs' function = 'SDL_Init' @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') class SDL_ttf(Library): libName = 'SDL_ttf' makeName = 'SDL_TTF' header = '' function = 'TTF_OpenFont' dependsOn = ('SDL', 'FREETYPE') @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') @classmethod def getVersion(cls, platform, linkStatic, distroRoot): def execute(cmd, log): version = cmd.expand(log, cls.getHeaders(platform), 'SDL_TTF_MAJOR_VERSION', 'SDL_TTF_MINOR_VERSION', 'SDL_TTF_PATCHLEVEL', ) return None if None in version else '%s.%s.%s' % version return execute class SQLite(Library): libName = 'sqlite3' makeName = 'SQLITE' header = '' function = 'sqlite3_prepare_v2' @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'darwin', 'dingux') @classmethod def getVersion(cls, platform, linkStatic, distroRoot): def execute(cmd, log): version = cmd.expand( log, cls.getHeaders(platform), 'SQLITE_VERSION' ) return None if version is None else version.strip('"') return execute class TCL(Library): libName = 'tcl' makeName = 'TCL' header = '' function = 'Tcl_CreateInterp' @classmethod def isSystemLibrary(cls, platform): return platform in ('android',) @classmethod def getTclConfig(cls, platform, distroRoot): '''Tcl has a config script that is unlike the typical lib-config script. Information is gathered by sourcing the config script, instead of executing it and capturing the queried value from stdout. This script is located in a library directory, not in a directory in the PATH. Also, it does not have the executable bit set. This method returns the location of the Tcl config script, or None if it could not be found. ''' if hasattr(cls, 'tclConfig'): # Return cached value. return cls.tclConfig def iterLocations(): if platform == 'android': # Under Android, the tcl set-up apparently differs from # other cross-platform setups. the search algorithm to find the # directory that will contain the tclConfig.sh script and the shared libs # is not applicable to Android. Instead, immediately return the correct # subdirectories to the routine that invokes iterLocations() sdl_android_port_path = environ['SDL_ANDROID_PORT_PATH'] libpath = sdl_android_port_path + '/project/libs/armeabi' yield libpath tclpath = sdl_android_port_path + '/project/jni/tcl8.5/unix' yield tclpath else: if distroRoot is None or cls.isSystemLibrary(platform): roots = ('/usr/local', '/usr') else: roots = (distroRoot, ) for root in roots: if isdir(root): for libdir in ('lib', 'lib64', 'lib/tcl'): libpath = root + '/' + libdir if isdir(libpath): yield libpath for entry in listdir(libpath): if entry.startswith('tcl8.'): tclpath = libpath + '/' + entry if isdir(tclpath): yield tclpath tclConfigs = {} log = open('derived/tcl-search.log', 'w') print >> log, 'Looking for Tcl...' try: for location in iterLocations(): path = location + '/tclConfig.sh' if isfile(path): print >> log, 'Config script:', path text = captureStdout( log, "sh -c '. %s && echo %s'" % ( path, '$TCL_MAJOR_VERSION $TCL_MINOR_VERSION' ) ) if text is not None: try: # pylint: disable-msg=E1103 major, minor = text.split() version = int(major), int(minor) except ValueError: pass else: print >> log, 'Found: version %d.%d' % version tclConfigs[path] = version try: # Minimum required version is 8.5. # Pick the oldest possible version to minimize the risk of # running into incompatible changes. tclConfig = min( ( version, path ) for path, version in tclConfigs.iteritems() if version >= (8, 5) )[1] except ValueError: tclConfig = None print >> log, 'No suitable versions found.' else: print >> log, 'Selected:', tclConfig finally: log.close() cls.tclConfig = tclConfig return tclConfig @classmethod def evalTclConfigExpr(cls, platform, distroRoot, expr, description): tclConfig = cls.getTclConfig(platform, distroRoot) if tclConfig is None: return None log = open('derived/tcl-search.log', 'a') try: print >> log, 'Getting Tcl %s...' % description text = captureStdout( log, shjoin([ 'sh', '-c', '. %s && eval "echo \\"%s\\""' % (tclConfig, expr) ]) ) if text is not None: print >> log, 'Result: %s' % text.strip() finally: log.close() return None if text is None else text.strip() @classmethod def getCompileFlags(cls, platform, linkStatic, distroRoot): if platform == 'android': # Use the current ANDROID cross-compilation flags and not the TCL flags. Otherwise, the # wrong version of libstdc++ will end-up on the include path; the minimal Android NDK # version instead of the more complete GNU version. This is because TCL for Android has # been configured with the minimal libstdc++ on the include path in the C(XX) flags and # not with the more complete GNU version return environ['ANDROID_CXXFLAGS'] wantShared = not linkStatic or cls.isSystemLibrary(platform) # The -DSTATIC_BUILD is a hack to avoid including the complete # TCL_DEFS (see 9f1dbddda2) but still being able to link on # MinGW (tcl.h depends on this being defined properly). return cls.evalTclConfigExpr( platform, distroRoot, '${TCL_INCLUDE_SPEC}' + ('' if wantShared else ' -DSTATIC_BUILD'), 'compile flags' ) @classmethod def getLinkFlags(cls, platform, linkStatic, distroRoot): if platform == 'android': # Use the current ANDROID cross-compilation flags and not the TCL flags to # prevent issues with libstdc++ version. See also getCompileFlags() return environ['ANDROID_LDFLAGS'] # Tcl can be built as a shared or as a static library, but not both. # Check whether the library type of Tcl matches the one we want. wantShared = not linkStatic or cls.isSystemLibrary(platform) tclShared = cls.evalTclConfigExpr( platform, distroRoot, '${TCL_SHARED_BUILD}', 'library type (shared/static)' ) log = open('derived/tcl-search.log', 'a') try: if tclShared == '0': if wantShared: print >> log, ( 'Dynamic linking requested, but Tcl installation has ' 'static library.' ) return None elif tclShared == '1': if not wantShared: print >> log, ( 'Static linking requested, but Tcl installation has ' 'dynamic library.' ) return None else: print >> log, ( 'Unable to determine whether Tcl installation has ' 'shared or static library.' ) return None finally: log.close() # Now get the link flags. if wantShared: return cls.evalTclConfigExpr( platform, distroRoot, '${TCL_LIB_SPEC}', 'dynamic link flags' ) else: return cls.evalTclConfigExpr( platform, distroRoot, '${TCL_EXEC_PREFIX}/lib/${TCL_LIB_FILE} ${TCL_LIBS}', 'static link flags' ) @classmethod def getVersion(cls, platform, linkStatic, distroRoot): return cls.evalTclConfigExpr( platform, distroRoot, '${TCL_MAJOR_VERSION}.${TCL_MINOR_VERSION}${TCL_PATCH_LEVEL}', 'version' ) class Theora(Library): libName = 'theoradec' makeName = 'THEORA' header = '' function = 'th_decode_ycbcr_out' dependsOn = ('OGG', ) @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') class Vorbis(Library): libName = 'vorbis' makeName = 'VORBIS' header = '' function = 'vorbis_synthesis_pcmout' dependsOn = ('OGG', ) @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'dingux') class ZLib(Library): libName = 'z' makeName = 'ZLIB' header = '' function = 'inflate' @classmethod def isSystemLibrary(cls, platform): return platform in ('android', 'darwin', 'dingux') @classmethod def getVersion(cls, platform, linkStatic, distroRoot): def execute(cmd, log): version = cmd.expand(log, cls.getHeaders(platform), 'ZLIB_VERSION') return None if version is None else version.strip('"') return execute # Build a dictionary of libraries using introspection. def _discoverLibraries(localObjects): for obj in localObjects: if isinstance(obj, type) and issubclass(obj, Library): if not (obj is Library): yield obj.makeName, obj librariesByName = dict(_discoverLibraries(locals().itervalues())) def allDependencies(makeNames): '''Compute the set of all directly and indirectly required libraries to build and use the given set of libraries. Returns the make names of the required libraries. ''' # Compute the reflexive-transitive closure. transLibs = set() newLibs = set(makeNames) while newLibs: transLibs.update(newLibs) newLibs = set( depMakeName for makeName in newLibs for depMakeName in librariesByName[makeName].dependsOn if depMakeName not in transLibs ) return transLibs openmsx-0.10.0/build/platform-mingw32.mk0000644000175000017500000000075012262345041020606 0ustar manuelmanuel00000000000000# Configuration for MinGW on x86 machines. # Does platform support symlinks? USE_SYMLINK:=false # File name extension of executables. EXEEXT:=.exe LIBRARYEXT:=.dll # Compiler flags. COMPILE_FLAGS+= \ -mthreads -mms-bitfields \ -I/mingw/include -I/mingw/include/w32api \ -D__GTHREAD_HIDE_WIN32API \ -DFS_CASEINSENSE # Linker flags. LINK_FLAGS:= \ -L/mingw/lib -L/mingw/lib/w32api -lwsock32 -lwinmm -ldsound -lsecur32 \ -mconsole -static-libgcc -static-libstdc++ \ $(LINK_FLAGS) openmsx-0.10.0/build/custom.mk0000644000175000017500000000151112262345041017004 0ustar manuelmanuel00000000000000# Customize openMSX to your system/preferences. # Directory to install to. # openMSX is always installed into a single self-contained directory. # But you can change that directory to for example /usr/local/openMSX # or /usr/games/openMSX if you like. INSTALL_BASE:=/opt/openMSX # Add revision number to executable file name? This applies only to # development versions, not to release versions (see version.py). VERSION_EXEC:=false # Create a symbolic link to the installed binary? # This link is placed in a location that is typically in a user's path: # /usr/local/bin for system-wide installs and ~/bin for personal installs. # This setting is only relevant on systems that support symbolic links. SYMLINK_FOR_BINARY:=true # Install content of Contrib/ directory? # Currently this contains a version of C-BIOS. INSTALL_CONTRIB:=true openmsx-0.10.0/build/entry.mk0000644000175000017500000000243712262345041016643 0ustar manuelmanuel00000000000000# Entry point of build system. # # Do a sanity check on the given action(s) and processes them sequentially. # Sequential processing is needed because for example "clean" and "all" cannot # run in parallel. Some actions might be able to run in parallel, but that is # an optimization we can do later, if it is really worth it. # All actions we want to expose to the user. USER_ACTIONS:=\ 3rdparty all app bindist clean createsubs dist install probe run \ staticbindist # Mark all actions as logical targets. .PHONY: $(USER_ACTIONS) # Reject unknown actions. UNKNOWN_ACTIONS:=$(filter-out $(USER_ACTIONS),$(MAKECMDGOALS)) ifneq ($(UNKNOWN_ACTIONS),) ifeq ($(words $(UNKNOWN_ACTIONS)),1) $(error Unknown action: $(UNKNOWN_ACTIONS)) else $(error Unknown actions: $(UNKNOWN_ACTIONS)) endif endif ifeq ($(MAKECMDGOALS),) # Make default action explicit. MAKECMDGOALS:=all .PHONY: default default: all endif ifeq ($(words $(MAKECMDGOALS)),1) # Single action, run it in this Make process. include build/main.mk else # Multiple actions are given, process them sequentially. # If the same action is given more than once, some warnings will be displayed, # but they will all be executed. ACTION_COUNTER:=$(MAKECMDGOALS:%=x) include build/entry-seq.mk $(MAKECMDGOALS): sequence-$(words $(MAKECMDGOALS)) @true endif openmsx-0.10.0/build/package-windows/0000755000175000017500000000000012262345041020226 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/package-windows/node.mk0000644000175000017500000000023012262345041021477 0ustar manuelmanuel00000000000000include build/node-start.mk SUBDIRS:= \ images DIST:= \ controlpanel.wxi openmsx.wxs openmsx1033.wxl *.py \ package.cmd include build/node-end.mk openmsx-0.10.0/build/package-windows/images/0000755000175000017500000000000012262345041021473 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/package-windows/images/node.mk0000644000175000017500000000011012262345041022741 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ *.jpg include build/node-end.mk openmsx-0.10.0/build/package-windows/images/TopBanner.jpg0000644000175000017500000000417412262345041024073 0ustar manuelmanuel00000000000000ÿØÿàJFIFddÿìDucky<ÿîAdobedÀÿÛ„       ÿÀ:íÿÄ!1"AQqaBRr#23s1A!Qq¡"2‘áÑñRÿÚ ?êb󜫌`[³™k<[eÿRÞO©úwÚü‰uè½þÚ;ÄÌBN//‰ËZ6÷{ý›ú2âÚFMª{žÅrß]«8´bYÊY Èó Âqì¦fF,ŒÆZOxèÓ»’"µ>:I5kë¼WÎb™kÛ,oä¼_wÉð‘f$ÉYYågËÉRnÏ5d|mz?y‰[M Fè] Z9ßû·á^si¥zº~xñþÿÕškëW9ÆSéÿ6àlœxrI’Ã;‰‘FÙ-äX‘’ut;¶î•S[}ÕV–]¿VýW‹ý]1˜™å?º9ð˜ob¥ o¬YÜ~Ó.Is{+cIñ÷–Írõ’âæ'EmNê®s“·³©ÓÙÖm¶¸ó†-Á¥p™oXðüSظtl±¹c]al¨ød†J¹’BÄzí®·9‰F;¯^ÚâýóÙ­mÖ¥öìÙ[ÚÎþï·wu‹³•ÿRÆÅ+eØjJž6±¬éG¹Ú•Ý:¢¿øýc·Õj^÷ž¾Sœ~óÏ–1•{§ª|")ÅywåX˜ò¸+è¯mdFëÛr+£zµ·+QjÇ¢/V¯Q·U©8´a¬NY‚6@®Fªµ*äE¢vªÌžñ ÿ6çyž}ÏR[‹¬=Û­¬¬®uhŠõ¾'éÝ–ÍsQ÷­{¡sßï¥)õ}¸ñŸ4u‰ã.“È_Úcì.oï$Hlí"|÷3;³#ª÷¹~ J”õ¬Ìâ8¤hÛÏî§ÞM’¸³³½óH*Ìuœñµ­¸W"éz½Ž{Xƪxµu÷"û-¿ãm‹DN1çäÔ†ô#ÔëÞê-­Ä²ªb36×1fi•ôlÔíXžíU÷U=¥¸i‹ëÇ:ðiIÄ»äòéÀüîMøì{§­|Îsc…®­Î^ª´ý-«¾G¹÷ÑÚè¶ÙŒôòó™ð„š©×h…'˯1SÉ”‚'Ú_\,×6úµ1ïDe\ŽUÔkZÔDNÉÝVˆ¾oÛ¿—Fí‘MµŠÄóŽ_?¯Ë:vvs˜\y"Óñ¼–Y)k•´šÕÓGGQ“ƬÔßbÓUOm®ó[E£”åÅ0ä<—ôûÕ }N´ºÆ^·R£ZÉåêÚôr¤‘5©ÓÙ¨ôSïZíÆ&úr™aýeäGîcÉ26—=¥Õ¾6 ÌÇ«n¢Ô“ÈôcQºj”j/ÄâÝî¦#Â[E0ëÏ2Çy™}T>]µ¿õ»ÙÚ¦­ÍÊèѧ®ªÐ§ÂDs8¶d¬l«¢J£á“¾™Ù~ÅüNNÿ³¯s¦Ú­ÂÑþ%¾»Ím o‘†kY_i{Ü”]Q»³›ÙU«ù›øŸ#ï}³wi~›ÇÂyOÂWš¶Vñà¶ñH²Ø;;ŸÐm¹¬µk¿Æ‘vŸRŽþ$oJ§ÍTúǵÚóÛkõ>î˜Ê—|G\ã†Vz)Þ‰Fõ³eôß'ˆ»º’Ò;·@Ö¾+ÜæNÇ£®V£ë§µP‹wu^Þ“²ßm[ë×7´V8ÊgÓðïü»Ëô?í_.òýš.ƦÛÿ¥|?¸ÇæSÓõ³ôc9gÒ·WF>¥¼™ÂîÂÆñˆË»x®XÕÔÖÌÆ½}èŽE11‘䘌s_®(vù–:_¹#Vêù›dC¿À\:ÞTÄä®q—r;^ò;êYUïX®7EïáÒµö‘ì¬Ìb'¦|Ù¬ÄOŒeCÈziÍò—‰&O)otƺ–Y&‘[©©[ FÈØê¥| ‡žî½Ÿ¹ßhõ6æ¿ |¸,5wšéM|W¯µ1ÿjý¹®O¥ÙÚÞªîk®­Úþ­ÏÄ»üZz^–>ŒaÅêÛ¯«žY£¥ÿÙopenmsx-0.10.0/build/package-windows/images/SideBanner.jpg0000644000175000017500000002145512262345041024216 0ustar manuelmanuel00000000000000ÿØÿàJFIFddÿìDuckyPÿîAdobedÀÿÛ„      ÿÀ8íÿÄÈ  "12B# !RbAQr‚3Ca’Scs$t¢Â”%q‘²âƒ“£4Ddu•&V "2BRb1r‚’#3ð!Aaq¢²ÒâQ¡ÂòCS$‘±ác4ñsƒ“TÿÚ ?Ÿ¢ ] !äx©X8= èI#ÅJÀÄèWBHy*V$@ºCÈñR°q zÐ’GŠ•€‰Ю„òjÍ'<}â_³ênªàA¤Ô_H®$…Ì·Y-ÕüëNé5'ã½u¹‰˜â—׋.z~b¢³µ==ªê«vիɹݒ¯ˆws´Œ#mÖCÝ#‰±\ƒôÜíO2Ãìqª>7‹€Ú¸Ê“S–Ô;#ÖEÃ帮«Îhòw{R牅³çLÊ\s«Ä줕¬4£µ.îsîÒ¹’˵.â.e‚PÜ/¯Èe:¥ÿww÷gÛqznT’¤«j¿f•nO—Èm¹öGo›Z÷Û_y-¯?ñ!¥SL%†OB©RV”©*Ü•y’¤û<V½vZùvI]†â¹]n¾V+W.ÖÉ~)OƒÙSî}ªðOº’J4¥V¬iG¥§SÝ(•úNå29wt¼Ÿ~ö’ó<Ó)¬›tê¼ÊCN¦kí!*þ*qINŸ„è^!Pîд¡ ÈÇzMÜÅ»§­½æî`áûl Ä ñ,xϽŸY£mu’¥8ËM3)owQå=§§Bw)ÿ&‡3­Za*µe†1_F8צwÜ!„àùÞ`Ô+,†^ O.éìN uzù±á«VÝÖ2UåWMÏÛ|M~õ)0¹/3e¹ÄêSµ­ŠT÷£†P”~˜O’×·åÍ£›x£ åHXõ–-2‚©ÌÐ\#dÆ4K«kÏ·ìRUÓÜ…|HÕ*6:_W Ê~ì“>¹HÒNS†SÚÝ%? ¤;.›¿êa¶vG¸œì0ue&>ë}9}Šò3\ŸÚ§ä½ÖŸ[FŒ~ãr“½2©–¸ Ý·ÝÞ–Râ ´9Ï1Ûwl¬;X½eå)l6øÂ¤žjÓyÁœËJ¯vßÈa´VHkýbû)ž Ê2ÿR?iL·^xþ–ܧ•qw*äªV+uoÅdð9 %¯„üˆt’£­K®™)Ô'T¥;œq­Sã»ænNí›uéž#U³îºêÕŒjSÚôvq–$ý»e[•çßÇ_™«ØU½*KJ¶–—zò‡Qâ¥FŠ­©>ïUÔû}ß™å™Z癕([Õ§R…|ya–,SÅ‚—©†só¸Lç TimoKe€#®ÒŸÇ®àEÿìxœh–°!¥jóËŠÇÏ»ÿPÚœeJû®Ÿ+r÷7O.æo̱lÔ­.'üº²ÚûÞƒe¯mŽßdrŽ|ã S·Ée_9qYE Ù¶oÑB—o£(i ^­º¨-<†ÜòømqI>Ú¥k;ŽïŧÄó¢ÓørÉäW¹.k·î™³NU´Bã±ÌÙCXµnô•‘Ó҆ǎŸg•´ø«ÃâÜ}“ÚBÎÒ`ÅU–9=}%7ü‰ÇÿÖÝíþsG5çO÷þŒWvÛ©95DàóUƒ5\7Ë6«k˜mô§—ù’Õ{ëWüÒç/Ž+ªQíÇüTËuç·j‡SÃøÍvÞ“yW…'Ùäoq¼´©,dÕU!Ûga¸„©>¹¸î3µ_µKq¼›µ ç5òvc<öâ­ôåùeîƬeþÖã cKEXu4Ê3ìÏ‹òK %•ÝC…ò?Y½8/ºgõ“1¨9’ð»üÆ’]B*ò(îÃi—æ0¦Ú¦%ENÕ+ÞêC#äÞgä«ÿÌ#oÞ)GeÖü<ÝþÖã/^îÞò1a\\‡Þÿm¼onýÞ|‹+¸©Ý2º†;öiiÑ)ö¨û¿SôÍ%𫘳:\jVøiÿ©±õe·ú—•s;xtš» O—²ÄMÃjÝãá+¸Éó ¶Å¬×aLTÖFm…+FÛT„¥]§ÏJ6/bw6‡†w™m-õgÞ³ ÙpéÒþ>§ÒðW›Ãâ>ñäÌŽ®E’ÚåõjñªQ§Ê]f¡sWVSz?úDßUXö¯"–´½gåöˆ·‰§¼Ï©K/³»ùŸa¨sŒeß±öSÛî%HÕSïú“ó½vÙ˜b®OGùÏ—¡IÆqª”«æ®,Õœ…iíѶã8¤øýå¡?”Ø9RÒW9…?ѯWñ"¯-PyñíW;€æ\ú²Çƒ+^aŠÎbCü)j‹SRê<èõRtC›µWô)mÅ©?©ñ:?7~WqaV×2Xááõ¾n‚Îße¢PH¯p?OPdçµõTŠŸjÒ»‰Ó\,vdÿ :ÏÂÕ}Fà:óŸùyJé+wÊwÞAñæaq›rí.ø•­#»Z;uié^;û?͇¥UäŒç(³½â摯tz¨Cr~lÚ¯[ÚNC—Û;„;Ç(Æ.,w¦ô˜‰jb»ÃGaɪåKKz}»™OCúWPŸ1F]Í5n§å9\K« ¯û³–Å/Oo« :]Cšùƒ’ÿ.«JÞΜ«TņQ§Àáu;{=HlO¥¼š®ì'‡0"Gg85n_Áæ§Ï‰tÓRŸõ‰öÌqö½Ù§ÉòUµ¦þRT½ÎºöûË–W¶7RÌ.*WR8vw)RþT>ÔçÒŸÑÎUêBqÁÖæ/£÷egXpþEsÆŠFž‚žSʸ¤ÜÊÕ+L}jw{7z…mûš>Ïœ.iûØâø|?BÆT"ÑîÚrCúm÷2öÏì»GÇüH×6p¬ÚÇЗTˆ6ñµNÕ),9¹.y:©mkù^Ã=˜Ð†}gÅ·ÞÃ({)m=༙Ç\\ý¿gXþwYifdì~Æ5‹Lº¤îKn®2Üѵx|*ûNoRœ¡ñIx¿J-¼§( Â±ÛŒ³*´KŽãñ›qk)[f;Iܵ«_ä=„%9aˆóÿÏWÎD²¿~»€±úìW¯’âXÈï£i:ÊÅ)ÕIK+Fc6¯{o>éÒòÿ°RÇ{<=˜¬çuÕ`^Þ8gš>£ÜÕaœsYie„bêa¬×1VˆkÁ­êqªj†’eJÑJV½6ö·ï«MëÓ}Í®IJVö¿¿?»—©×{J{ÏL¸. ‰q¶-M…àÔqŒcgHõu‘¢ZB}º©^Õ)JV»”¥y”¯2ŽwqqVâxêË—qŽ¥àãm¼ÚÚu [KNÕ¡^d©*""£DZídÿ¢®¤Ö^©õ~‚+Qº½4íFîšS»j}…Œ`>áXßYšj§ø/‹ò#¡w•¹â+àÉÛæD9•“]’߉Ȭ›—"Ê]ÿeos¸„.Ýû‹äÚù»=Àç«¥½ dØËªW£µ»s±ŸOüêýä+Ìt,ë)¥KÖ´§ëÓ†yoæþ6Åy; “Õ¤Ê!¢F‘Ö¤©è¯øm~#û||aÏ(âw–Ó¶«*SÞ‹!bebÝP\úÉòEæ?Ç|MÆu²]‡SÈv––Y­+o¨j…0zÜûÈÕÙº9úM ß|=´…kÙÕ—ðãö–×rÕá¸mÿ$f¾‹EõÙci¢ž.ºøi×”æh¥xû©OŽå+óÿ0æ£o>×à´¥ §²Þ߸Oíç‰ñN-ÄZÝ‚>ŸÄ­5FˆvÂÁßK˜÷‡Äóž?¢¨ö'C…]ÜÊâ®=,”bÍ„ €k½‡u½¶Tezav\߆ÂÉtwX®×.ÙÓéVÅ4óÛºM¹»ËµJÑEÖŒ¶ï…ÆáK‡Ö§YÞ]•|*÷í¦Î®4uK‘dë©C 0”îSªu^T§o›qj©åïêKÝÍGqý>Çó5™Æ|h¹ …o§•ÖoíCòÚþ¥´·Ógóù×ñ_”29YQ•z»óû õq£%Z›]Y KoÒ¹Y\{˯p†Cd½0žXWû…——ò¡d-#äjÞïwÕ¶žŠ¾ó#Ÿó~_ƇxŽôWT%©éˆç«°Óõí‡&îC‡*ÝÀ£¦ q½“¶ô´ª^ÕXÄ}®”Èmj­võµ·Ý÷6|Fs—sËn¸lÉZxâŽ_¤§ÚëÏ“ÈYÞ5:’Ó†ëÓM¢Ö+‘eDº¸JÐê–ÓÉJÐãqq>éûß3ͼ.a쥳/Þ—ìz¨èRÂôX¿ÊiK¡õBîç…¸.¿Äf®³,å›i™´az¶üJÈÍulaIýâ·¶Ï踭M£“rxfWø*îGiÅLyo×S¼ÏãÌwÂó5ßÒðÍŸ#ÜËãLukUf)ÕÚÆÅ/GÓëOÌ}¶Õû6ÜR’ßÀjty~Îy\`ö‹Ž,°áa%(ÈUš—Jµ,jÉSëPßYâ÷tù$ÅÀ¹¢˜ÌúÉ+jš~:ÒãjÓoâI¯‚qÁ5q{€ã<¹y÷`™ËµîÕ½–Ð×[½Y!;ŽäÈÈyM+Ex}©RöœnpÃ&A~ž†lèU±^™c1˜0ãé½ér–›B×à’‰KÑžÇ.1ë¼k•ó²¹ù?-rž]•Ê­jSNÌf'†ÂšÑ]N›,ÄNßúEýì£ì¡?µ·ûJ`ßÍPIúÑâVS°¾ Îmk«Æ..éìŸu.ܱö7ÅZá¾ø}s^Îh­®ã²óç®§Z«6;F‡Nº–5d¯F‡Rµ,jÍ^½K³U¡pbMQÊ˱hy2ÖŒnM¼&²´¾’Óo¡2v¹µ[UÓÝæÚa¯jOo$‹Ý,°àÁ‡ ¹”G¯„ËLÁŽÖ›[C(N‰m ü)OÊ×Ï ›åQð¼G'Ë$Æ\ÔcÕÒ&· \—ZoU4ÃZëãæy{[OâÔ¶º¸½)U–ìtbUâGÛÜa¥ÃÖ9fMe{›ÃgUÛe³QÑ×¥-(êª*-bLn=MÝ6b%¦Ó÷‰sÏ3Ö½YÕž*R–ÍMÿ1Ïj‡—±‡12ªì¬cÍEмÞT?¦–Ïë%ïÕ:yÿÙmÒöžvÊÏ»0Þkôî£ë;9‰ò Iꩆk-=ëJ}Ý›6¢¶•+ûM¿ˆ’Ÿ;ÛÏ~‰Ý¤ÆµßKþögÊC¸–5#K÷§MÈè”Ò?Ùl$9þˆ©Í–]£6åpÿÑžùùPl¹Ó“a××£jæb¸‚"K¾>ÖÕc1 ¶ßê°á‚¼æÉÏÝA,h2ÇÔ··N$âÎÍñø¼{‹³‰Ããì² µaköHvÑ–KÓ\Vç$8¤¶Ÿ˜µnò˜Ì®ú¯x”§µ‰Tà–Þ)™&ñÄù®®LÙø½<™’Vå­×a4¥­J×Ûª•©†­¿$̆Pð¯½ÇË~/@JYZáLʱÆmä£Û­,ãºÒõü*Û-ëúfâÿ^½àïpeêôþ¦%æ_ƒ l,§ƒð7çæS…XR1†uz®¶KMZ‹ –a® Ý"öô¯ª¿©])*ùjVÕ+Ó©^ëŸÕ:îí)Ì4Ãúz³ÃO(ËùU~J±ý¾ÇnCV—M™;]äÕ¯™ÆÖ–/Y'ÿ†KÄî%nSö8eÚTšwÝS¾e9 Ôª#ÊW›cIu~gO²ù#˜gåñ_N\:¾|>þóEÌ-{µ]Ö뛊Ä“í#þ±¹½mm˜ÞëÈUÎq˜D\Û¼Þ–­§_}ßÕSŒ§õ‹ü³ÞâQ5¿ôØï;—;‡ä ·²œr’àø|yXÜšæœKõêˆüX1a¾êÜ×­«Í)Ç­©ó7ömÓ]§—”#ã{&X±T·2|v¯0Ç®ñ{ÈúɧÈ`¿_dÊUªV¦$6¦×±Z{ªðWÙ©áÇ †‹Enæ’eŽ”¯~OŒmjÂFÎ’l#+¦³m(ýܤ§w—Ýwª×÷“êræc*Z=Ôö©Ë³Õó¡äÿ‹ËnãsKIú³HÂɺ”áVX±ØÔœ»Åw޲…ÔäØ`K+ý’ë²7°•'ÿp‡´ÿj³¹x!›ÊŽcVÒ[µ©âô¡øqµîb¡Ž”'Õl4\zÎ?5á–¹w#dÕR1úyÔ˜Î/¤´·ŽemÈÜêdÊkgžÊ+iùˆÝæÛÕkɽ(ú‘¦6_rž ìJ@…¬‡\eΕSäÍoŽä£¼Æüº°Ü[‡´Ksšøºž«¢Ê“§ŽíÈû¥þ_So ‰¶[é©ÛþÝx56¹}w¡äŽPu›¬’+Ÿc°a¥>`¯_…m¶¥8â~òöëî‘]×Ç7±Héj¨òWÂäÑd17J\¦“zñüêlÑß ^%¿õ£½µ=F÷~D©;V”«Mk™ùb˘-{½Ô|Ùt¡.ÊêÎîvÓÇ ÌrÊšÕXöW^¼"NíQkê±- ûTü;™-þ™?½CKò#sO!æ\½?kTº5#»ø[ÍŽeJósyÅNfUÏ¢g6ϰÜSY,¶åu”ÂÉwuSŸ[*Nß¿3Héó|;¾é×üÊ+\ç¹þ(Ëëlaýl0WŒ(aë7Þò‚—%«“KÕC¼©š³+g4‡ã»µ[“¹§7'_1õ“CbȦUÇù-3xœ™™ZIÖ=Æ-g+Y¨”´/§6²d¥õUvš6äW­©Vøûvt ÒÍ€v¦Ýäd5ºàøçi%h¾Ï/«æÁôŽô¥D‹ŽÊfáË©>ê[‘;;¾ó¨5^qÎ¥”e•+ÂXjt|æW%±ï—Q„·VÇ÷–¿”×q_+KEì«ä½¦ȲÔgf½¥>íu¬v–Q+¢Û޶ó)KN¥+NÆ–”õq<•Ïóèi¥WFˆWÉòKGZ?7Ò¹Îr9ØíÃj›|€À€³òÜ3ÍꞥÉ+X±„¿:[y¦ÝØ´ë®ÇÕJ“¢“ðëáöÕ§ ±Á8â‹ØËV¬û_½…"R±®G¹UcÞHt.ˌǤGøûËé)_Ö~©Ï/¼,È.êñ{¾6RŒ}_ºËÓÎî¡ 8×¶Ù–á¼VçÃlo”Ó·wOE“ÚËuŸÙjüér £¦Ÿ2z(Œ–Ò•«¥Ò7\§)µÊép­á†ŸUޝ^U¥ŠK§KþwÆü·Ü}IÉV¾6¸m’k'¼ŸÉþç¼[qÛÿåTe´Æœ.8<’ÚÙß{ƒfx¬”ûÐeS»d¿ûZ%Ù3ÿxy€cÛþpȱü¶(¼S˜fØ=¢4ê_RPÙÇŸRê=ïYŘþ¡•{z‘”§~‚½óÌ"çÜoN‘¤ùF‡¹ö+Éd—­“úu–úD”ŸÖh¤};Žzàì~:¥Þó.Oצd5Ì#þUÈÐ {Ì{ÕÃRÓ°8r‚Ë–®×↭º/ÔãqÕù~Úc)ë'üoþ©ªgå•ePöµuË«©ü<æVÓ&º¹Ü‡ÄÒIsó,å×92Ã)‹‘gy,<¹ÏÇS¨®Nõ·O ˜ä(íõ7|Nu|îõ|È84sE\ÞûL/#†”uÆ1C·Û—êÕäüß2¼®6”½–ô—ß =ešsbÒñÙÔòé¦IÉï5_Iæ= LU¡±%…+Ê©Ò"·óÚ”•+Êl¾äZ%˜éº…LtáŸFœsù'þXþ–?™¯¿¥áNe$¿ŸA¹ðò?áܯ=Œfu)Ÿ¨ƒ)>YPßO¸üg|ÛU§óTŸ"Ò¤nIiwgJՎ¸éKJ¬¨ËQû’v£È¸ÕÌÂq<’éÑÿ†zµèr$'áB˜“Ñ‚ò¾ó‰”ÇöI9&{áuZÿ¥Ô°õ*bû_…¶YsD)ûê^« WÄäªl‚ú« âJKÈ›Q#ü³ghÓ[Д¿ OÖ59¿+‰ê%Iso›oÀiÙ—†Ù¬(ÂP¥J;2Ã(ü}¿“ÌþÍlÍcµÅ­ÏÎjÆÎ+-¥>e8ã;]R•ækÏ»tö|‰šfZ¡uK…-JŸ6%îÃ?“GĦ¶{km·Fx»)!ànOW[YÝÙ3‘òZ¦—”^0Ö­Gi†7zZè(WŠ‘?QjñW™Ç·U·~Äw>^Èmò[H[Òô¥Ö“EÌs ·•x³lQX€ÿÙopenmsx-0.10.0/build/package-windows/harvest.py0000644000175000017500000001355112262345041022261 0ustar manuelmanuel00000000000000# Generates Windows resource header. from optparse import OptionParser from os import walk from os.path import ( basename, dirname, isfile, join as joinpath, relpath, split as splitpath ) from uuid import uuid4 indentSize = 2 excludedDirectories = ['.svn', 'CVS'] excludedFiles = ['node.mk'] # Bit of a hack, but it works def isParentDir(child, parent): return '..' not in relpath(child, parent) def makeFileId(guid): return 'file_' + guid def makeDirectoryId(guid): return 'directory_' + guid def makeComponentId(guid): return 'component_' + guid def newGuid(): return str(uuid4()).replace('-', '') def walkPath(sourcePath): if isfile(sourcePath): yield dirname(sourcePath), [], [ basename(sourcePath) ] else: for dirpath, dirnames, filenames in walk(sourcePath): yield dirpath, dirnames, filenames class WixFragment(object): def __init__(self, fileGenerator, componentGroup, directoryRef, virtualDir, excludedFile, win64): self.fileGenerator = fileGenerator self.componentGroup = componentGroup self.directoryRef = directoryRef self.virtualDir = virtualDir self.indentLevel = 0 self.win64 = 'yes' if win64 else 'no' if excludedFile: # TODO: Modifying this global variable is an unexpected side effect. excludedFiles.append(excludedFile) def incrementIndent(self): self.indentLevel += indentSize def decrementIndent(self): self.indentLevel -= indentSize def indent(self, line): return ' ' * self.indentLevel + line def startElement(self, elementName, **args): line = self.indent( '<%s %s>' % ( elementName, ' '.join('%s="%s"' % item for item in args.iteritems()) ) ) self.incrementIndent() return line def endElement(self, elementName): self.decrementIndent() return self.indent('' % elementName) def yieldFragment(self): # List that stores the components we've added components = [] # Stack that stores directories we've already visited stack = [] # List that stores the virtual directories we added virtualstack = [] yield self.indent( '' ) yield self.startElement( 'Wix', xmlns = 'http://schemas.microsoft.com/wix/2006/wi' ) yield self.startElement('Fragment') yield self.startElement('DirectoryRef', Id = self.directoryRef) # Add virtual directories if self.virtualDir: head = self.virtualDir while head: joinedPath = head head, tail_ = splitpath(head) virtualstack.insert(0, joinedPath) for path in virtualstack: componentId = 'directory_' + str(uuid4()).replace('-', '_') yield self.startElement( 'Directory', Id = componentId, Name = basename(path) ) # Walk the provided file list firstDir = True for dirpath, dirnames, filenames in self.fileGenerator: # Remove excluded sub-directories for exclusion in excludedDirectories: if exclusion in dirnames: dirnames.remove(exclusion) if firstDir: firstDir = False else: # Handle directory hierarchy appropriately while stack: popped = stack.pop() if isParentDir(dirpath, popped): stack.append(popped) break else: yield self.endElement('Directory') # Enter new directory stack.append(dirpath) yield self.startElement( 'Directory', Id = makeDirectoryId(newGuid()), Name = basename(dirpath) ) # Remove excluded files for exclusion in excludedFiles: if exclusion in filenames: filenames.remove(exclusion) # Process files for filename in filenames: guid = newGuid() componentId = makeComponentId(guid) sourcePath = joinpath(dirpath, filename) yield self.startElement( 'Component', Id = componentId, Guid = guid, DiskId = '1', Win64 = self.win64 ) yield self.startElement( 'File', Id = makeFileId(guid), Name = filename, Source = sourcePath ) yield self.endElement('File') yield self.endElement('Component') components.append(componentId) # Drain pushed physical directories while stack: popped = stack.pop() yield self.endElement('Directory') # Drain pushed virtual directories while virtualstack: popped = virtualstack.pop() yield self.endElement('Directory') yield self.endElement('DirectoryRef') yield self.endElement('Fragment') # Emit ComponentGroup yield self.startElement('Fragment') yield self.startElement('ComponentGroup', Id = self.componentGroup) for component in components: yield self.startElement('ComponentRef', Id = component) yield self.endElement('ComponentRef') yield self.endElement('ComponentGroup') yield self.endElement('Fragment') yield self.endElement('Wix') def generateWixFragment( sourcePath, componentGroup, directoryRef, virtualDir, excludedFile, win64 ): fileGenerator = walkPath(sourcePath) wf = WixFragment( fileGenerator, componentGroup, directoryRef, virtualDir, excludedFile, win64 ) return wf.yieldFragment() def run(): parser = OptionParser() parser.add_option( '-c', '--componentGroup', type = 'string', dest = 'componentGroup' ) parser.add_option( '-r', '--directoryRef', type = 'string', dest = 'directoryRef' ) parser.add_option( '-s', '--sourcePath', type = 'string', dest = 'sourcePath' ) parser.add_option( '-v', '--virtualDir', type = 'string', dest = 'virtualDir' ) parser.add_option( '-x', '--excludedFile', type = 'string', dest = 'excludedFile' ) parser.add_option( '-w', '--win64', type = 'string', dest = 'win64' ) options, args_ = parser.parse_args() for line in generateWixFragment( options.sourcePath, options.componentGroup, options.directoryRef, options.virtualDir, options.excludedFile, options.win64 ): print line if __name__ == '__main__': run() openmsx-0.10.0/build/package-windows/vs_menu2013.py0000644000175000017500000000475412262345041022574 0ustar manuelmanuel00000000000000import msvcrt import os #set standard settings (No XP support/64Bit version) compileXP = 0 #XP support 0 = off/1 = on version = 64 #32 = win32/64 = x64 version quit = 0 #Loop variable keep at 0 #Some standard strings location='C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\' #location of Visual Studio 2012 (express) VC files title=' openMSX build menu for Visual Studio 2013' menuval = { '[1] Update from Repository' ,'[2] Get 3rd Party Files\n' ,'[3] Compile 3rd-party files' ,'[4] Compile openMSX Release' ,'[5] Generate Installer and Packages\n' ,'[6] Toggle Win32/Win64 Version' ,'[7] Toggle Toggle XP Support\n' ,'[8] Quit\n' } def menu(): os.system('color 9f') os.system('mode 80,24') os.system('TITLE'+title) os.system('cls') print '-'*50 print title print '-'*50+'\n' for item in sorted(menuval): print item print '-'*50 print ' Resulting Binary settings:' if version == 32: print ' - Windows 32bit Version' else: print ' - Windows 64bit Version' if compileXP == 1:print ' - XP Support ON' else: print ' - XP Support Off' print '-'*50 print if compileXP == 1: print('The DirectX June 2010 SDK is needed for XP support') if quit == 1: os.system('cls') def execCommand(cmd): os.system('cls') os.chdir('..\\..\\') os.system(cmd) os.chdir('build\\package-windows') os.system('pause') def updateSourceCode(): execCommand('git pull') def update3rdparty(): execCommand('call "python" build\\thirdparty_download.py windows') def compile(whattocompile): compiler = 'x86' if version == 32 else 'x86_amd64' support = 'v120' if compileXP == 0 else 'v120_xp' compilefor = 'Win32' if version == 32 else 'x64' compilewhat = 'build\\msvc\\openmsx.sln' if whattocompile == 'OpenMSX' else 'build\\3rdparty\\3rdparty.sln' cmd = 'call "'+location+'vcvarsall.bat" '+str(compiler)+'&&' cmd += 'set PlatformToolset='+str(support)+'&&' cmd += 'msbuild -p:Configuration=Release;Platform='+str(compilefor)+' '+compilewhat+' /m' execCommand(cmd) def package(): packagefor = 'Win32' if version == 32 else 'x64' execCommand('build\\package-windows\\package.cmd '+packagefor+' release ..\\wxcatapult') # Main Loop menu() while quit == 0: x = ord(msvcrt.getch()) if x == 49: updateSourceCode() if x == 50: update3rdparty() if x == 51: compile('3rdParty') if x == 52: compile('OpenMSX') if x == 53: package() if x == 54: version = 32 if version == 64 else 64 if x == 55: compileXP = 0 if compileXP == 1 else 1 if x == 56: quit = 1 menu() openmsx-0.10.0/build/package-windows/packagezip.py0000644000175000017500000000522612262345041022723 0ustar manuelmanuel00000000000000from packagewindows import PackageInfo, generateInstallFiles from os.path import abspath, basename, exists, join as joinpath, relpath from zipfile import ZIP_DEFLATED, ZipFile import os, sys def addFile(zipFile, path, zipPath): print 'Adding ' + path zipFile.write(path, zipPath, ZIP_DEFLATED) def addDirectory(zipFile, root, zipPath): for path, dirs, files in os.walk(root): if '.svn' in dirs: dirs.remove('.svn') # don't visit .svn directories for name in files: thisZipPath = zipPath if abspath(root) != abspath(path): thisZipPath = joinpath(thisZipPath, relpath(path, root)) addFile(zipFile, joinpath(path, name), joinpath(thisZipPath, name)) def packageZip(info): print 'Generating install files...' generateInstallFiles(info) if not exists(info.packagePath): os.mkdir(info.packagePath) zipFileName = info.packageFileName + '-bin.zip' zipFilePath = joinpath(info.packagePath, zipFileName) if exists(zipFilePath): os.unlink(zipFilePath) print 'Generating ' + zipFilePath zipFile = ZipFile(zipFilePath, 'w') addDirectory(zipFile, joinpath(info.makeInstallPath, 'doc'), 'doc') addDirectory(zipFile, joinpath(info.makeInstallPath, 'share'), 'share') addDirectory(zipFile, info.codecPath, 'codec') addFile(zipFile, info.openmsxExePath, basename(info.openmsxExePath)) addFile( zipFile, joinpath(info.sourcePath, 'resource\\openmsx.ico'), 'share\\icons\\openmsx.ico' ) addFile(zipFile, info.catapultExePath, 'Catapult\\bin\\Catapult.exe') addDirectory(zipFile, joinpath(info.catapultPath, 'doc'), 'Catapult\\doc') addDirectory( zipFile, joinpath(info.catapultPath, 'resources\\bitmaps'), 'Catapult\\resources\\bitmaps' ) addDirectory( zipFile, joinpath(info.catapultBuildPath, 'install\\dialogs'), 'Catapult\\resources\\dialogs' ) addFile( zipFile, joinpath(info.catapultSourcePath, 'catapult.xpm'), 'Catapult\\resources\\icons\\catapult.xpm' ) addFile( zipFile, joinpath(info.catapultPath, 'README'), 'Catapult\\doc\\README' ) zipFile.close() zipFileName = info.packageFileName + '-pdb.zip' zipFilePath = joinpath(info.packagePath, zipFileName) if exists(zipFilePath): os.unlink(zipFilePath) print 'Generating ' + zipFilePath zipFile = ZipFile(zipFilePath, 'w') addFile(zipFile, info.openmsxPdbPath, basename(info.openmsxPdbPath)) addFile(zipFile, info.catapultPdbPath, basename(info.catapultPdbPath)) zipFile.close() if __name__ == '__main__': if len(sys.argv) == 4: packageZip(PackageInfo(*sys.argv[1 : ])) else: print >> sys.stderr, 'Usage: python packagezip.py ' \ 'platform configuration catapultPath' sys.exit(2) openmsx-0.10.0/build/package-windows/openmsx.wxs0000644000175000017500000002744412262345041022475 0ustar manuelmanuel00000000000000 NEWPRODUCTFOUND PREVIOUSVERSIONSINSTALLED = 500]]> 1 NOT Installed WixVariable Id="WixUILicenseRtf" Value="license1033.rtf" /--> openmsx-0.10.0/build/package-windows/packagewindows.py0000644000175000017500000000634212262345041023613 0ustar manuelmanuel00000000000000from install import installAll from version import ( extractRevisionNumber, getVersionedPackageName, packageVersionNumber, releaseFlag ) from os import makedirs, remove, rmdir, sep, walk from os.path import exists, join as joinpath import sys def emptyOrCreateDirectory(top): if exists(top): _emptyDirectory(top) else: makedirs(top) def _emptyDirectory(top): for root, dirs, files in walk(top, topdown = False): for name in files: remove(joinpath(root, name)) for name in dirs: rmdir(joinpath(root, name)) def generateInstallFiles(info): emptyOrCreateDirectory(info.makeInstallPath) installAll( info.makeInstallPath + sep, 'bin', 'share', 'doc', info.openmsxExePath, 'mingw32', True, True ) class PackageInfo(object): def __init__(self, platform, configuration, catapultPath): self.platform = platform.lower() if self.platform == 'win32': self.cpu = 'x86' self.platform = 'Win32' self.win64 = False elif self.platform == 'x64': self.cpu = 'x64' self.platform = 'x64' self.win64 = True else: raise ValueError('Wrong platform: ' + platform) self.configuration = configuration.lower() if self.configuration == 'release': self.configuration = 'Release' self.catapultConfiguration = 'Unicode Release' elif self.configuration == 'developer': self.configuration = 'Developer' self.catapultConfiguration = 'Unicode Debug' elif self.configuration == 'debug': self.configuration = 'Debug' self.catapultConfiguration = 'Unicode Debug' else: raise ValueError('Wrong configuration: ' + configuration) self.catapultPath = catapultPath # Useful variables self.buildFlavor = self.platform + '-VC-' + self.configuration self.buildPath = joinpath('derived', self.buildFlavor) self.sourcePath = 'src' self.codecPath = 'Contrib\\codec\\Win32' self.packageWindowsPath = 'build\\package-windows' self.catapultSourcePath = joinpath(self.catapultPath, 'src') self.catapultBuildFlavor = '%s-VC-%s' % ( self.platform, self.catapultConfiguration ) self.catapultBuildPath = joinpath( self.catapultPath, joinpath('derived', self.catapultBuildFlavor) ) self.catapultExePath = joinpath( self.catapultBuildPath, 'install\\catapult.exe' ) self.catapultPdbPath = joinpath( self.catapultBuildPath, 'install\\catapult.pdb' ) self.openmsxExePath = joinpath(self.buildPath, 'install\\openmsx.exe') self.openmsxPdbPath = joinpath(self.buildPath, 'install\\openmsx.pdb') self.packagePath = joinpath(self.buildPath, 'package-windows') self.makeInstallPath = joinpath(self.packagePath, 'install') self.version = packageVersionNumber if releaseFlag: self.version += '.0' else: self.version += '.%d' % extractRevisionNumber() # -----.ext self.os = 'windows' self.compiler = 'vc' self.packageFileName = '-'.join(( getVersionedPackageName(), self.os, self.compiler, self.cpu )) if __name__ == '__main__': if len(sys.argv) == 4: PackageInfo(*sys.argv[1 : ]) else: print >> sys.stderr, \ 'Usage: python packagewindows.py ' \ 'platform configuration catapultPath' sys.exit(2) openmsx-0.10.0/build/package-windows/controlpanel.wxi0000644000175000017500000000236612262345041023466 0ustar manuelmanuel00000000000000 openmsx-0.10.0/build/package-windows/openmsx1033.wxl0000644000175000017500000000270012262345041022761 0ustar manuelmanuel00000000000000 1033 openMSX The MSX emulator that aims for perfection http://openmsx.sourceforge.net openMSX Manual openMSX Website Uninstall openMSX Catapult Launcher and GUI for openMSX Catapult Manual Core Emulator Catapult UI ZMBV Video Codec Codec for viewing movies generated by openMSX This feature requires [4] on your hard drive. It has [2] of [3] subfeatures selected. Zip Motion Block Video [ZMBV] A newer version of openMSX is already installed openMSX is only supported on Windows 2000 and above openmsx-0.10.0/build/package-windows/packagemsi.py0000644000175000017500000001664312262345041022716 0ustar manuelmanuel00000000000000from harvest import generateWixFragment from packagewindows import ( PackageInfo, emptyOrCreateDirectory, generateInstallFiles ) from os import environ, mkdir, system, unlink from os.path import exists, join as joinpath from zipfile import ZIP_DEFLATED, ZipFile import sys def _writeFragment( wxsFile, sourcePath, componentGroup, directoryRef, virtualDir, excludedFile, win64 ): print 'Generating ' + wxsFile out = open(wxsFile, 'w') try: out.writelines( '%s\n' % line for line in generateWixFragment( sourcePath, componentGroup, directoryRef, virtualDir, excludedFile, win64 ) ) finally: out.close() def packageMSI(info): print 'Generating install files...' generateInstallFiles(info) wixIntermediatePath = joinpath(info.buildPath, 'build\\WiX') emptyOrCreateDirectory(wixIntermediatePath) if not exists(info.packagePath): mkdir(info.packagePath) print 'Generating fragments...' # openMSX files openMSXExeFile = joinpath(wixIntermediatePath, 'openmsxexe.wxs') openMSXExeObjFile = joinpath(wixIntermediatePath, 'openmsxexe.wixobj') sourcePath = joinpath(info.makeInstallPath, 'bin\\openmsx.exe') _writeFragment( openMSXExeFile, sourcePath, 'openMSXExe', 'OPENMSXINSTALLDIR', None, None, info.win64 ) openMSXDocFile = joinpath(wixIntermediatePath, 'openmsxdoc.wxs') openMSXDocObjFile = joinpath(wixIntermediatePath, 'openmsxdoc.wixobj') sourcePath = joinpath(info.makeInstallPath, 'doc') _writeFragment( openMSXDocFile, sourcePath, 'openMSXDoc', 'OPENMSXINSTALLDIR', 'doc', None, info.win64 ) openMSXShareFile = joinpath(wixIntermediatePath, 'openmsxshare.wxs') openMSXShareObjFile = joinpath(wixIntermediatePath, 'openmsxshare.wixobj') sourcePath = joinpath(info.makeInstallPath, 'share') _writeFragment( openMSXShareFile, sourcePath, 'openMSXShare', 'OPENMSXINSTALLDIR', 'share', None, info.win64 ) openMSXIconFile = joinpath(wixIntermediatePath, 'openmsxicon.wxs') openMSXIconObjFile = joinpath(wixIntermediatePath, 'openmsxicon.wixobj') sourcePath = joinpath(info.sourcePath, 'resource\\openmsx.ico') _writeFragment( openMSXIconFile, sourcePath, 'openMSXIcon', 'OPENMSXINSTALLDIR', 'share\\icons', None, info.win64 ) # ZMBV files ZMBVCodecFile = joinpath(wixIntermediatePath, 'zmbvcodec.wxs') ZMBVCodecObjFile = joinpath(wixIntermediatePath, 'zmbvcodec.wixobj') sourcePath = joinpath(info.codecPath, 'zmbv.dll') _writeFragment( ZMBVCodecFile, sourcePath, 'ZMBVCodec', 'SystemFolder', None, None, False ) ZMBVFilesFile = joinpath(wixIntermediatePath, 'zmbvfiles.wxs') ZMBVFilesObjFile = joinpath(wixIntermediatePath, 'zmbvfiles.wixobj') sourcePath = info.codecPath _writeFragment( ZMBVFilesFile, sourcePath, 'ZMBVFiles', 'OPENMSXINSTALLDIR', 'codec', 'zmbv.dll', info.win64 ) # Catapult files catapultBinFile = joinpath(wixIntermediatePath, 'catapultbin.wxs') catapultBinObjFile = joinpath(wixIntermediatePath, 'catapultbin.wixobj') sourcePath = joinpath(info.catapultBuildPath, 'install\\catapult.exe') _writeFragment( catapultBinFile, sourcePath, 'CatapultBin', 'OPENMSXINSTALLDIR', 'Catapult\\bin', None, info.win64 ) catapultDocFile = joinpath(wixIntermediatePath, 'catapultdoc.wxs') catapultDocObjFile = joinpath(wixIntermediatePath, 'catapultdoc.wixobj') sourcePath = joinpath(info.catapultPath, 'doc') _writeFragment( catapultDocFile, sourcePath, 'CatapultDoc', 'OPENMSXINSTALLDIR', 'Catapult\\doc', 'release-process.txt', info.win64 ) catapultBitmapsFile = joinpath(wixIntermediatePath, 'catapultbitmaps.wxs') catapultBitmapsObjFile = joinpath( wixIntermediatePath, 'catapultbitmaps.wixobj' ) sourcePath = joinpath(info.catapultPath, 'resources\\bitmaps') _writeFragment( catapultBitmapsFile, sourcePath, 'CatapultBitmaps', 'OPENMSXINSTALLDIR', 'Catapult\\resources\\bitmaps', 'release-process.txt', info.win64 ) catapultDialogsFile = joinpath(wixIntermediatePath, 'catapultdialogs.wxs') catapultDialogsObjFile = joinpath( wixIntermediatePath, 'catapultdialogs.wixobj' ) sourcePath = joinpath(info.catapultBuildPath, 'install\\dialogs') _writeFragment( catapultDialogsFile, sourcePath, 'CatapultDialogs', 'OPENMSXINSTALLDIR', 'Catapult\\resources\\dialogs', None, info.win64 ) catapultIconsFile = joinpath(wixIntermediatePath, 'catapulticons.wxs') catapultIconsObjFile = joinpath(wixIntermediatePath, 'catapulticons.wixobj') sourcePath = joinpath(info.catapultBuildPath, 'src\\catapult.xpm') _writeFragment( catapultIconsFile, sourcePath, 'CatapultIcons', 'OPENMSXINSTALLDIR', 'Catapult\\resources\\icons', None, info.win64 ) catapultReadmeFile = joinpath(wixIntermediatePath, 'catapultreadme.wxs') catapultReadmeObjFile = joinpath( wixIntermediatePath, 'catapultreadme.wixobj' ) sourcePath = joinpath(info.catapultPath, 'README') _writeFragment( catapultReadmeFile, sourcePath, 'CatapultReadme', 'OPENMSXINSTALLDIR', 'Catapult\\doc', None, info.win64 ) # Variables needed inside the WiX scripts: # OPENMSX_VERSION to tell it the product version # OPENMSX_ICON_PATH to locate the MSI's control panel icon # OPENMSX_PACKAGE_WINDOWS_PATH to locate the bmps used in the UI environ.update( OPENMSX_VERSION = info.version, OPENMSX_ICON_PATH = info.openmsxExePath, OPENMSX_PACKAGE_WINDOWS_PATH = info.packageWindowsPath, ) openMSXFile = joinpath(info.packageWindowsPath, 'openmsx.wxs') openMSXObjFile = joinpath(wixIntermediatePath, 'openmsx.wixobj') candleCmd = ' '.join(( 'candle.exe', '-arch %s' % info.cpu, '-o "%s\\\\"' % wixIntermediatePath, '-ext WixUtilExtension', '"%s"' % openMSXFile, '"%s"' % openMSXExeFile, '"%s"' % openMSXDocFile, '"%s"' % openMSXShareFile, '"%s"' % openMSXIconFile, '"%s"' % ZMBVCodecFile, '"%s"' % ZMBVFilesFile, '"%s"' % catapultBinFile, '"%s"' % catapultDocFile, '"%s"' % catapultBitmapsFile, '"%s"' % catapultDialogsFile, '"%s"' % catapultIconsFile, '"%s"' % catapultReadmeFile, )) # Run Candle print candleCmd system(candleCmd) msiFileName = info.packageFileName + '-bin.msi' msiFilePath = joinpath(info.packagePath, msiFileName) if exists(msiFilePath): unlink(msiFilePath) print 'Generating ' + msiFilePath lightCmd = ' '.join(( 'light.exe', '-o "%s"' % msiFilePath, '-sw1076', '-ext WixUtilExtension', '-ext WixUIExtension', '-loc "%s"' % joinpath(info.packageWindowsPath, 'openmsx1033.wxl'), '"%s"' % openMSXObjFile, '"%s"' % openMSXExeObjFile, '"%s"' % openMSXDocObjFile, '"%s"' % openMSXShareObjFile, '"%s"' % openMSXIconObjFile, '"%s"' % ZMBVCodecObjFile, '"%s"' % ZMBVFilesObjFile, '"%s"' % catapultBinObjFile, '"%s"' % catapultDocObjFile, '"%s"' % catapultBitmapsObjFile, '"%s"' % catapultDialogsObjFile, '"%s"' % catapultIconsObjFile, '"%s"' % catapultReadmeObjFile, )) # Run Light print lightCmd system(lightCmd) # Zip up the MSI zipFileName = info.packageFileName + '-bin-msi.zip' zipFilePath = joinpath(info.packagePath, zipFileName) print 'Generating ' + zipFilePath zipFile = ZipFile(zipFilePath, 'w') zipFile.write(msiFilePath, msiFileName, ZIP_DEFLATED) zipFile.close() if __name__ == '__main__': if len(sys.argv) == 4: packageMSI(PackageInfo(*sys.argv[1 : ])) else: print >> sys.stderr, 'Usage: python packagemsi.py ' \ 'platform configuration catapultPath' sys.exit(2) openmsx-0.10.0/build/package-windows/vs_menu.py0000644000175000017500000000475412262345041022266 0ustar manuelmanuel00000000000000import msvcrt import os #set standard settings (No XP support/64Bit version) compileXP = 0 #XP support 0 = off/1 = on version = 64 #32 = win32/64 = x64 version quit = 0 #Loop variable keep at 0 #Some standard strings location='C:\\Program Files (x86)\\Microsoft Visual Studio 11.0\\VC\\' #location of Visual Studio 2012 (express) VC files title=' openMSX build menu for Visual Studio 2012' menuval = { '[1] Update from Repository' ,'[2] Get 3rd Party Files\n' ,'[3] Compile 3rd-party files' ,'[4] Compile openMSX Release' ,'[5] Generate Installer and Packages\n' ,'[6] Toggle Win32/Win64 Version' ,'[7] Toggle Toggle XP Support\n' ,'[8] Quit\n' } def menu(): os.system('color 9f') os.system('mode 80,24') os.system('TITLE'+title) os.system('cls') print '-'*50 print title print '-'*50+'\n' for item in sorted(menuval): print item print '-'*50 print ' Resulting Binary settings:' if version == 32: print ' - Windows 32bit Version' else: print ' - Windows 64bit Version' if compileXP == 1:print ' - XP Support ON' else: print ' - XP Support Off' print '-'*50 print if compileXP == 1: print('The DirectX June 2010 SDK is needed for XP support') if quit == 1: os.system('cls') def execCommand(cmd): os.system('cls') os.chdir('..\\..\\') os.system(cmd) os.chdir('build\\package-windows') os.system('pause') def updateSourceCode(): execCommand('git pull') def update3rdparty(): execCommand('call "python" build\\thirdparty_download.py windows') def compile(whattocompile): compiler = 'x86' if version == 32 else 'x86_amd64' support = 'v110' if compileXP == 0 else 'v110_xp' compilefor = 'Win32' if version == 32 else 'x64' compilewhat = 'build\\msvc\\openmsx.sln' if whattocompile == 'OpenMSX' else 'build\\3rdparty\\3rdparty.sln' cmd = 'call "'+location+'vcvarsall.bat" '+str(compiler)+'&&' cmd += 'set PlatformToolset='+str(support)+'&&' cmd += 'msbuild -p:Configuration=Release;Platform='+str(compilefor)+' '+compilewhat+' /m' execCommand(cmd) def package(): packagefor = 'Win32' if version == 32 else 'x64' execCommand('build\\package-windows\\package.cmd '+packagefor+' release ..\\wxcatapult') # Main Loop menu() while quit == 0: x = ord(msvcrt.getch()) if x == 49: updateSourceCode() if x == 50: update3rdparty() if x == 51: compile('3rdParty') if x == 52: compile('OpenMSX') if x == 53: package() if x == 54: version = 32 if version == 64 else 64 if x == 55: compileXP = 0 if compileXP == 1 else 1 if x == 56: quit = 1 menu() openmsx-0.10.0/build/package-windows/package.cmd0000644000175000017500000000212612262345041022307 0ustar manuelmanuel00000000000000@echo off rem rem **** Run this from the top of the openMSX source tree: **** rem rem Usage: package.cmd OPENMSX_PLATFORM OPENMSX_CONFIGURATION CATAPULT_BASEPATH rem rem **** OPENMSX_PLATFORM is { Win32, x64 } **** rem **** OPENMSX_CONFIGURATION is { Release, Developer, Debug } **** rem **** CATAPULT_BASEPATH is an absolute or relative path; e.g. ..\wxCatapult **** if "%3" == "" goto usage if "%4" NEQ "" goto usage setlocal set OPENMSX_PLATFORM=%1 echo OPENMSX_PLATFORM is %OPENMSX_PLATFORM% set OPENMSX_CONFIGURATION=%2 echo OPENMSX_CONFIGURATION is %OPENMSX_CONFIGURATION% set CATAPULT_BASEPATH=%3 echo CATAPULT_BASEPATH is %CATAPULT_BASEPATH% set OPENMSX_PACKAGE_WINDOWS_PATH=.\build\package-windows set PYTHONPATH=%PYTHONPATH%;.\build python %OPENMSX_PACKAGE_WINDOWS_PATH%\packagezip.py %OPENMSX_PLATFORM% %OPENMSX_CONFIGURATION% %CATAPULT_BASEPATH% python %OPENMSX_PACKAGE_WINDOWS_PATH%\packagemsi.py %OPENMSX_PLATFORM% %OPENMSX_CONFIGURATION% %CATAPULT_BASEPATH% endlocal goto end :usage echo Usage: package.cmd platform configuration catapultPath :end openmsx-0.10.0/build/makeutils.py0000644000175000017500000000763112262345041017522 0ustar manuelmanuel00000000000000from os.path import isdir import re def filterLines(lines, regex): '''Filters each line of the given line iterator using the given regular expression string. For each match, a tuple containing the text matching each capture group from the regular expression is yielded. ''' matcher = re.compile(regex) for line in lines: if line.endswith('\n'): line = line[ : -1] match = matcher.match(line) if match: yield match.groups() def filterFile(filePath, regex): '''Filters each line of the given text file using the given regular expression string. For each match, a tuple containing the text matching each capture group from the regular expression is yielded. ''' inp = open(filePath, 'r') try: for groups in filterLines(inp, regex): yield groups finally: inp.close() def joinContinuedLines(lines): '''Iterates through the given lines, replacing lines that are continued using a trailing backslash with a single line. ''' buf = '' for line in lines: if line.endswith('\\\n'): buf += line[ : -2] elif line.endswith('\\'): buf += line[ : -1] else: yield buf + line buf = '' if buf: raise ValueError('Continuation on last line') _reEval = re.compile('(\$\(|\))') def evalMakeExpr(expr, makeVars): '''Evaluates variable references in an expression. Raises ValueError if there is a syntax error in the expression. Raises KeyError if the expression references a non-existing variable. ''' stack = [ [] ] for part in _reEval.split(expr): if part == '$(': stack.append([]) elif part == ')' and len(stack) != 1: name = ''.join(stack.pop()) if name.startswith('addprefix '): prefix, args = name[len('addprefix') : ].split(',') prefix = prefix.strip() value = ' '.join(prefix + arg for arg in args.split()) elif name.startswith('addsuffix '): suffix, args = name[len('addsuffix') : ].split(',') suffix = suffix.strip() value = ' '.join(arg + suffix for arg in args.split()) elif name.startswith('shell '): # Unsupported; assume result is never used. value = '?' elif name.startswith('call DIR_IF_EXISTS,'): # This is our function, not a Make function, but the goal is # not to emulate Make, so we emulate our function instead. path = name[len('call DIR_IF_EXISTS,'): ] value = path if isdir(path) else '' elif name.isdigit(): # This is a function argument; assume the evaluated result is # never used. value = '?' else: value = makeVars[name] stack[-1].append(value) else: stack[-1].append(part) if len(stack) != 1: raise ValueError('Open without close in "%s"' % expr) return ''.join(stack.pop()) def extractMakeVariables(filePath, makeVars = None): '''Extract all variable definitions from the given Makefile. The optional makeVars argument is a dictionary containing the already defined variables. These variables will be included in the output; the given dictionary is not modified. Returns a dictionary that maps each variable name to its value. ''' makeVars = {} if makeVars is None else dict(makeVars) inp = open(filePath, 'r') try: for name, assign, value in filterLines( joinContinuedLines(inp), r'[ ]*([A-Za-z0-9_]+)[ ]*([+:]?=)(.*)' ): if assign == '=': makeVars[name] = value.strip() elif assign == ':=': makeVars[name] = evalMakeExpr(value, makeVars).strip() elif assign == '+=': # Note: Make will or will not evaluate the added expression # depending on how the variable was originally defined, # but we don't store that information. makeVars[name] = makeVars[name] + ' ' + value.strip() else: assert False, assign finally: inp.close() return makeVars def parseBool(valueStr): '''Parses a string containing a boolean value. Accepted values are "true" and "false"; anything else raises ValueError. ''' if valueStr == 'true': return True elif valueStr == 'false': return False else: raise ValueError('Invalid boolean "%s"' % valueStr) openmsx-0.10.0/build/systemfuncs.py0000644000175000017500000000263712262345041020110 0ustar manuelmanuel00000000000000class SystemFunction(object): name = None @classmethod def getFunctionName(cls): return cls.name @classmethod def getMakeName(cls): return cls.name.upper() @classmethod def iterHeaders(cls, targetPlatform): raise NotImplementedError class FTruncateFunction(SystemFunction): name = 'ftruncate' @classmethod def iterHeaders(cls, targetPlatform): yield '' class ClockGetTimeFunction(SystemFunction): name = 'clock_gettime' @classmethod def iterHeaders(cls, targetPlatform): yield '' class MMapFunction(SystemFunction): name = 'mmap' @classmethod def iterHeaders(cls, targetPlatform): if targetPlatform in ('darwin', 'openbsd'): yield '' yield '' class PosixMemAlignFunction(SystemFunction): name = 'posix_memalign' @classmethod def iterHeaders(cls, targetPlatform): yield '' class USleepFunction(SystemFunction): name = 'usleep' @classmethod def iterHeaders(cls, targetPlatform): yield '' class NftwFunction(SystemFunction): name = 'nftw' @classmethod def iterHeaders(cls, targetPlatform): yield '' # Build a list of system functions using introspection. def _discoverSystemFunctions(localObjects): for obj in localObjects: if isinstance(obj, type) and issubclass(obj, SystemFunction): if obj is not SystemFunction: yield obj systemFunctions = list(_discoverSystemFunctions(locals().itervalues())) openmsx-0.10.0/build/flavour-m68k.mk0000644000175000017500000000031112262345041017730 0ustar manuelmanuel00000000000000# ARM specific optimization flags # Optimisation flags: # overrule optimization level for m68k # TODO: is this still strictly needed? CXXFLAGS+=-O1 -DNDEBUG # Strip executable? OPENMSX_STRIP:=true openmsx-0.10.0/build/cpu.py0000644000175000017500000000417312262345041016311 0ustar manuelmanuel00000000000000class CPU(object): '''Abstract base class for CPU families. ''' # String that we use to identify this CPU type. name = None # Big (True) or little (False) endian? # Note that some CPUs can do both; if both settings are used in practice # we treat them like different CPUs (see MIPS/MIPSel). bigEndian = None # Allow unaligned memory accesses? unalignedMemoryAccess = False # GCC flags to pass to the compile and link commands. gccFlags = () class Alpha(CPU): '''DEC Alpha. ''' name = 'alpha' bigEndian = False class ARM(CPU): '''ARM. ''' name = 'arm' bigEndian = False class AVR32(CPU): '''Atmel AVR32, an embedded RISC CPU. ''' name = 'avr32' bigEndian = True class HPPA(CPU): '''HP PA-RISC. ''' name = 'hppa' bigEndian = True class IA64(CPU): '''Intel Itanium. ''' name = 'ia64' bigEndian = False class M68k(CPU): '''Motorola 680x0. ''' name = 'm68k' bigEndian = True class MIPS(CPU): '''Big endian MIPS. ''' name = 'mips' bigEndian = True class MIPSel(MIPS): '''Little endian MIPS. ''' name = 'mipsel' bigEndian = False class PPC(CPU): '''32-bit Power PC. ''' name = 'ppc' bigEndian = True class PPC64(CPU): '''64-bit Power PC. ''' name = 'ppc64' bigEndian = True class S390(CPU): '''IBM S/390. ''' name = 's390' bigEndian = True class SH(CPU): '''Little endian Renesas SuperH. ''' name = 'sh' bigEndian = False class SHeb(CPU): '''Big endian Renesas SuperH. ''' name = 'sheb' bigEndian = True class Sparc(CPU): '''Sun Sparc. ''' name = 'sparc' bigEndian = True class X86(CPU): '''32-bit x86: Intel Pentium, AMD Athlon etc. ''' name = 'x86' bigEndian = False unalignedMemoryAccess = True gccFlags = '-m32', class X86_64(CPU): '''64-bit x86. Also known as AMD64 or x64. ''' name = 'x86_64' bigEndian = False unalignedMemoryAccess = True gccFlags = '-m64', # Build a dictionary of CPUs using introspection. def _discoverCPUs(localObjects): for obj in localObjects: if isinstance(obj, type) and issubclass(obj, CPU): if not (obj is CPU): yield obj.name, obj _cpusByName = dict(_discoverCPUs(locals().itervalues())) def getCPU(name): return _cpusByName[name] openmsx-0.10.0/build/flavour-i686.mk0000644000175000017500000000036112262345041017644 0ustar manuelmanuel00000000000000# Configuration for "i686" flavour: # Optimised for Pentium 3, but compatible with Pentium 2 and higher. # Start with generic optimisation flags. include build/flavour-opt.mk # Add x86 specific flags. CXXFLAGS+=-march=i686 -mtune=pentium3 openmsx-0.10.0/build/package-arch/0000755000175000017500000000000012262345041017451 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/package-arch/PKGBUILD0000644000175000017500000000116612262345041020601 0ustar manuelmanuel00000000000000# PKGBUILD for openMSX # Contributor: Theo Smit pkgname=openmsx pkgver=0.6.2 pkgrel=1 pkgdesc="openMSX, a great opensource MSX emulator with lots of great features" arch=('i686') url="openmsx.sourceforge.net" license=('GPL') depends=(libxml2 gcc sdl_image tcl) makedepends=() provides=() conflicts=() replaces=() backup=() install= source=(http://downloads.sourceforge.net/$pkgname/$pkgname-$pkgver.tar.gz) noextract=() md5sums=('282acf2ea7bf67e15a7b8d961c9556a5') build() { cd $startdir/src/$pkgname-$pkgver ./configure make || return 1 make OPENMSX_INSTALL=$startdir/pkg/opt/openMSX install } openmsx-0.10.0/build/package-arch/node.mk0000644000175000017500000000012212262345041020722 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ PKGBUILD README include build/node-end.mk openmsx-0.10.0/build/package-arch/README0000644000175000017500000000215012262345041020327 0ustar manuelmanuel00000000000000PKGBUILD file for building an openMSX package for archlinux/i686, contributed by Theo Smit . This PKGBUILD was only tested on i686 (32-bit), if you want to build for an x86_64 machine you can try to modify the PKGBUILD accordingly. If you're lucky you only have to modify the "arch=" line. Usage: - Create a temporary working directory and go there: $ mkdir /tmp/build-openmsx $ cd /tmp/build-openmsx - Copy the PKGBUILD file there: $ cp path/to/PKGBUILD . - Run the "makepkg" tool: $ makepkg This will download the sources for openMSX 0.6.2, verify the dependencies, compile openMSX, install it in the temporary directory and archive that installation into a binary openMSX package. - Install the binary package with the standard Arch package manager: $ pacman -A openmsx-0.6.2-1-i686.pkg.tar.gz - Now you can start openMSX: $ /opt/openMSX/bin/openmsx Please read the documentation in /opt/openMSX/doc/manual to learn more about openMSX, for example how to install additional system ROMs. ------------------------------------------------------------------------------ openmsx-0.10.0/build/checksum.py0000644000175000017500000000337312262345041017325 0ustar manuelmanuel00000000000000from hashlib import new as newhash from os import stat from os.path import isfile import sys def verifyFile(filePath, fileLength, checksums): actualLength = stat(filePath).st_size if actualLength != fileLength: raise IOError( 'Expected length %d, actual length %d' % (fileLength, actualLength) ) hashers = {} for algo in checksums.iterkeys(): try: hashers[algo] = newhash(algo) except ValueError, ex: raise IOError('Failed to create "%s" hasher: %s' % (algo, ex)) inp = open(filePath, 'rb') bufSize = 16384 try: while True: buf = inp.read(bufSize) if not buf: break for hasher in hashers.itervalues(): hasher.update(buf) finally: inp.close() for algo, hasher in sorted(hashers.iteritems()): if checksums[algo] != hasher.hexdigest(): raise IOError('%s checksum mismatch' % algo) def main(filePath, fileLengthStr, checksumStrs): if not isfile(filePath): print >> sys.stderr, 'No such file: %s' % filePath sys.exit(2) try: fileLength = int(fileLengthStr) except ValueError: print >> sys.stderr, 'Length should be an integer' sys.exit(2) checksums = {} for checksumStr in checksumStrs: try: algo, hashval = checksumStr.split('=') except ValueError: print >> sys.stderr, 'Invalid checksum format: %s' % checksumStr sys.exit(2) else: checksums[algo] = hashval print 'Validating: %s' % filePath try: verifyFile(filePath, fileLength, checksums) except IOError, ex: print >> sys.stderr, 'Validation FAILED: %s' % ex sys.exit(1) else: print 'Validation passed' sys.exit(0) if __name__ == '__main__': if len(sys.argv) >= 3: main( sys.argv[1], sys.argv[2], sys.argv[3 : ] ) else: print >> sys.stderr, ( 'Usage: python checksum.py FILE LENGTH (ALGO=HASH)*' ) sys.exit(2) openmsx-0.10.0/build/platform-gnu.mk0000644000175000017500000000027112262345041020107 0ustar manuelmanuel00000000000000# Configuration for Debian GNU systems on various kernels (FreeBSD, Hurd). # This should be the same as compiling for a GNU system with a Linux kernel: include build/platform-linux.mk openmsx-0.10.0/build/android/0000755000175000017500000000000012262345041016563 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/android/node.mk0000644000175000017500000000017312262345041020042 0ustar manuelmanuel00000000000000include build/node-start.mk SUBDIRS:= \ openmsx DIST:= \ setup_anddev.sh \ *.mk \ *.patch include build/node-end.mk openmsx-0.10.0/build/android/tcl8.5.11_unix_Makefile.in.patch0000644000175000017500000000263312262345041024312 0ustar manuelmanuel00000000000000*** Makefile.in 2011-11-04 13:47:57.000000000 +0100 --- Makefile.in.android 2012-11-03 15:40:36.000000000 +0100 *************** *** 211,217 **** COMPAT_OBJS = @LIBOBJS@ ! AC_FLAGS = @DEFS@ AR = @AR@ RANLIB = @RANLIB@ DTRACE = @DTRACE@ --- 211,217 ---- COMPAT_OBJS = @LIBOBJS@ ! AC_FLAGS = -DPACKAGE_NAME=\"tcl\" -DPACKAGE_TARNAME=\"tcl\" -DPACKAGE_VERSION=\"8.5\" -DPACKAGE_STRING=\"tcl\ 8.5\" -DPACKAGE_BUGREPORT=\"\" -DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_UNISTD_H=1 -DNO_VALUES_H=1 -DHAVE_LIMITS_H=1 -DHAVE_SYS_PARAM_H=1 -DTCL_CFGVAL_ENCODING=\"iso8859-1\" -DSTATIC_BUILD=1 -DMODULE_SCOPE=extern\ __attribute__\(\(__visibility__\(\"hidden\"\)\)\) -DTCL_SHLIB_EXT=\".so\" -DTCL_CFG_OPTIMIZED=1 -DTCL_CFG_DEBUG=1 -DTCL_TOMMATH=1 -DMP_PREC=4 -DTCL_WIDE_INT_TYPE=long\ long -DHAVE_OPENDIR=1 -DHAVE_STRTOL=1 -DHAVE_WAITPID=1 -DNO_GETWD=1 -DHAVE_GETADDRINFO=1 -DNO_FD_SET=1 -DHAVE_SYS_TIME_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_GMTIME_R=1 -DHAVE_LOCALTIME_R=1 -DHAVE_MKTIME=1 -DHAVE_TM_GMTOFF=1 -DHAVE_STRUCT_STAT_ST_BLOCKS=1 -DHAVE_STRUCT_STAT_ST_BLKSIZE=1 -DHAVE_BLKCNT_T=1 -DHAVE_INTPTR_T=1 -DHAVE_UINTPTR_T=1 -DNO_UNION_WAIT=1 -DHAVE_SIGNED_CHAR=1 -DHAVE_SYS_IOCTL_H=1 -DTCL_UNLOAD_DLLS=1 -DTCL_CROSS_COMPILE=1 AR = @AR@ RANLIB = @RANLIB@ DTRACE = @DTRACE@ openmsx-0.10.0/build/android/tcl8.5.11_Android.mk0000644000175000017500000000125112262345041022010 0ustar manuelmanuel00000000000000LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := tcl8.5 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include ifneq ($(NDK_R5_TOOLCHAIN),) LOCAL_SRC_FILES := lib/$(TARGET_ARCH_ABI)/lib$(LOCAL_MODULE).so include $(PREBUILT_SHARED_LIBRARY) else LOCAL_SRC_FILES := dummy.c include $(BUILD_SHARED_LIBRARY) $(abspath $(LOCAL_PATH)/../../obj/local/armeabi/lib$(LOCAL_MODULE).so): $(LOCAL_PATH)/lib/armeabi/lib$(LOCAL_MODULE).so OVERRIDE_CUSTOM_LIB cp -f $< $@ $(abspath $(LOCAL_PATH)/../../obj/local/armeabi-v7a/lib$(LOCAL_MODULE).so): $(LOCAL_PATH)/lib/armeabi-v7a/lib$(LOCAL_MODULE).so OVERRIDE_CUSTOM_LIB cp -f $< $@ .PHONY: OVERRIDE_CUSTOM_LIB OVERRIDE_CUSTOM_LIB: endif openmsx-0.10.0/build/android/openmsx/0000755000175000017500000000000012262345041020254 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/android/openmsx/node.mk0000644000175000017500000000025512262345041021534 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ AndroidAppSettings.cfg.template.* \ AndroidBuild.sh generate_AndroidAppSettings.sh launch_anddev_build.sh include build/node-end.mk openmsx-0.10.0/build/android/openmsx/generate_AndroidAppSettings.sh0000755000175000017500000000614312262345041026233 0ustar manuelmanuel00000000000000#!/bin/bash echo "LAB:INFO Generating AndroidAppSettings.cfg from template file" # Read environment.props (if it exists) to get following two params: # sdl_android_port_path # my_home_dir if [ ! -f environment.props ]; then echo "LAB:ERROR: No file environment.props in $(pwd)" exit 1 fi . ./environment.props # Determine current revision and version name PYTHONPATH="${my_home_dir}/build" export PYTHONPATH cd "${my_home_dir}" # The (Android) version code must be an increasing number so that Android application # manager can recognize that it is a new version of the same application and take # appropriate action like retaining or migrating the user settings # The easiest way to get an increasing number for new builds in git is by # counting the number of commit messages. VERSION_CODE=$(git log --oneline | wc -l) # The (Android) version name can be any arbitrary string that is hopefully # meaningfull to the user. Best is to use the version package name # to be aligned with the version name used for builds for other platforms. VERSION_NAME=$(python -c "import version; print version.getVersionedPackageName()") # Return to the directory containing this script and the AndroidAppSettings files cd "${my_home_dir}/build/android/openmsx" # Determine version number of AndroidAppSettings supported # by current version of anddev Android build script CHANGE_APP_SETTINS_SCRIPT="${sdl_android_port_path}/changeAppSettings.sh" if [ ! -f "${CHANGE_APP_SETTINS_SCRIPT}" ]; then echo "LAB:ERROR: No such file ${CHANGE_APP_SETTINS_SCRIPT}." echo " Please follow instructions in compilation guide for android" echo " port to correctly set-up the android build environment." exit 1 fi CHANGE_APP_SETTINGS_VERSION=$(grep 'CHANGE_APP_SETTINGS_VERSION=[0-9][0-9]*' "${CHANGE_APP_SETTINS_SCRIPT}") CHANGE_APP_SETTINGS_VERSION=${CHANGE_APP_SETTINGS_VERSION#*=} if [ -z "${CHANGE_APP_SETTINGS_VERSION}" ]; then # Latest version of changeAppSettings.sh no longer contains an explicit version # number for the app-settings # However, it does have a mechanism to automagically upgrade to newer app-settings # with (hopefully sane) default values # As such, use latest versioned template when the changeAppSettings.sh does not # contain an explicit settings version CHANGE_APP_SETTINGS_VERSION=19 fi APP_SETTINGS_CFG="AndroidAppSettings.cfg" APP_SETTINGS_TEMPLATE="${APP_SETTINGS_CFG}.template.${CHANGE_APP_SETTINGS_VERSION}" if [ ! -f ${APP_SETTINGS_TEMPLATE} ]; then echo "LAB:ERROR: No such file ${APP_SETTINGS_TEMPLATE}." echo " Please create one manually. It can be based on one" echo " of the existing app settings template file" exit 1 fi cp ${APP_SETTINGS_TEMPLATE} ${APP_SETTINGS_CFG} . ${APP_SETTINGS_CFG} if [ "$AppVersionCode" != "${VERSION_CODE}" ]; then sed -i "s/^AppVersionCode=.*$/AppVersionCode=${VERSION_CODE}/" ${APP_SETTINGS_CFG} fi if [ "$AppVersionName" != "${VERSION_NAME}" ]; then sed -i "s/^AppVersionName=.*$/AppVersionName=${VERSION_NAME}/" ${APP_SETTINGS_CFG} fi echo "LAB:INFO AndroidAppSettings.cfg generated for version ${VERSION_CODE} with version name ${VERSION_NAME}" openmsx-0.10.0/build/android/openmsx/AndroidAppSettings.cfg.template.190000644000175000017500000003173212262345041026547 0ustar manuelmanuel00000000000000# The application settings for Android libSDL port AppSettingVersion=19 # libSDL version to use (1.2 or 1.3, specify 1.3 for SDL2) LibSdlVersion=1.2 # Specify application name (e.x. My Application) AppName="openMSX" # Specify reversed site name of application (e.x. com.mysite.myapp) AppFullName=org.openmsx.android.openmsx # Specify screen orientation: (v)ertical/(p)ortrait or (h)orizontal/(l)andscape ScreenOrientation=h # Do not allow device to sleep when the application is in foreground, set this for video players or apps which use accelerometer InhibitSuspend=y # Specify path to download application data in zip archive in the form 'Description|URL|MirrorURL^Description2|URL2|MirrorURL2^...' # If you'll start Description with '!' symbol it will be enabled by default, other downloads should be selected by user from startup config menu # If the URL in in the form ':dir/file.dat:http://URL/' it will be downloaded as binary BLOB to the application dir and not unzipped # If the URL does not contain 'http://' it is treated as file from 'project/jni/application/src/AndroidData' dir - # these files are put inside .apk package by build system # Also please avoid 'https://' URLs, many Android devices do not have trust certificates and will fail to connect to SF.net over HTTPS AppDataDownloadUrl="Application data|appdata.zip" # Video color depth - 16 BPP is the fastest and supported for all modes, 24 bpp is supported only # with SwVideoMode=y, SDL_OPENGL mode supports everything. (16)/(24)/(32) VideoDepthBpp=16 # Enable OpenGL depth buffer (needed only for 3-d applications, small speed decrease) (y) or (n) NeedDepthBuffer=n # Enable OpenGL stencil buffer (needed only for 3-d applications, small speed decrease) (y) or (n) NeedStencilBuffer=n # Try to use GLES 2.x context - will revert to GLES 1.X if unsupported by device # you need this option only if you're developing 3-d app (y) or (n) NeedGles2=n # Application uses software video buffer - you're calling SDL_SetVideoMode() without SDL_HWSURFACE and without SDL_OPENGL, # this will allow small speed optimization. Enable this even when you're using SDL_HWSURFACE. (y) or (n) SwVideoMode=y # Application video output will be resized to fit into native device screen (y)/(n) SdlVideoResize=y # Application resizing will keep 4:3 aspect ratio, with black bars at sides (y)/(n) SdlVideoResizeKeepAspect=y # Application does not call SDL_Flip() or SDL_UpdateRects() appropriately, or draws from non-main thread - # enabling the compatibility mode will force screen update every 100 milliseconds, which is laggy and inefficient (y) or (n) CompatibilityHacks=n # Application initializes SDL audio/video inside static constructors (which is bad, you won't be able to run ndk-gdb) (y)/(n) CompatibilityHacksStaticInit=n # On-screen Android soft text input emulates hardware keyboard, this will only work with Hackers Keyboard app (y)/(n) CompatibilityHacksTextInputEmulatesHwKeyboard=y # Hack for broken devices: prevent audio chopping, by sleeping a bit after pushing each audio chunk (y)/(n) CompatibilityHacksPreventAudioChopping=n # Hack for broken apps: application ignores audio buffer size returned by SDL (y)/(n) CompatibilityHacksAppIgnoresAudioBufferSize=n # Hack for VCMI: preload additional shared libraries before aplication start CompatibilityHacksAdditionalPreloadedSharedLibraries="" # Hack for Free Heroes 2, which redraws the screen inside SDL_PumpEvents(): slow and compatible SDL event queue - # do not use it with accelerometer/gyroscope, or your app may freeze at random (y)/(n) CompatibilityHacksSlowCompatibleEventQueue=n # Save and restore OpenGL state when drawing on-screen keyboard for apps that use SDL_OPENGL CompatibilityHacksTouchscreenKeyboardSaveRestoreOpenGLState= # Application uses mouse (y) or (n), this will show mouse emulation dialog to the user AppUsesMouse=y # Application needs two-button mouse, will also enable advanced point-and-click features (y) or (n) AppNeedsTwoButtonMouse=n # Show SDL mouse cursor, for applications that do not draw cursor at all (y) or (n) ShowMouseCursor=n # Force relative (laptop) mouse movement mode, useful when both on-screen keyboard and mouse are needed (y) or (n) ForceRelativeMouseMode=n # Application needs arrow keys (y) or (n), will show on-screen dpad/joystick (y) or (n) AppNeedsArrowKeys=y # Application needs text input (y) or (n), enables button for text input on screen AppNeedsTextInput=y # Application uses joystick (y) or (n), the on-screen DPAD will be used as joystick 0 axes 0-1 AppUsesJoystick=y # Application uses second on-screen joystick, as SDL joystick 0 axes 2-3 (y)/(n) AppUsesSecondJoystick=n # Application uses accelerometer (y) or (n), the accelerometer will be used as joystick 1 axes 0-1 and 5-7 AppUsesAccelerometer=n # Application uses gyroscope (y) or (n), the gyroscope will be used as joystick 1 axes 2-4 AppUsesGyroscope=n # Application uses multitouch (y) or (n), multitouch events are passed as SDL_JOYBALLMOTION events for the joystick 0 AppUsesMultitouch=n # Application records audio (it will use any available source, such a s microphone) # API is defined in file SDL_android.h: int SDL_ANDROID_OpenAudioRecording(SDL_AudioSpec *spec); void SDL_ANDROID_CloseAudioRecording(void); # This option will add additional permission to Android manifest (y)/(n) AppRecordsAudio=n # Application implements Android-specific routines to put to background, and will not draw anything to screen # between SDL_ACTIVEEVENT lost / gained notifications - you should check for them # rigth after SDL_Flip(), if (n) then SDL_Flip() will block till app in background (y) or (n) # This option is reported to be buggy, sometimes failing to restore video state NonBlockingSwapBuffers=y # Redefine common hardware keys to SDL keysyms # BACK hardware key is available on all devices, MENU is available on pre-ICS devices, other keys may be absent # SEARCH and CALL by default return same keycode as DPAD_CENTER - one of those keys is available on most devices # Use word NO_REMAP if you want to preserve native functionality for certain key (volume keys are 3-rd and 4-th) # Keys: TOUCHSCREEN (works only when AppUsesMouse=n), DPAD_CENTER/SEARCH, VOLUMEUP, VOLUMEDOWN, MENU, BACK, CAMERA RedefinedKeys="SPACE RETURN NO_REMAP NO_REMAP MENU WORLD_92 NO_REMAP" # Number of virtual keyboard keys (currently 6 is maximum) AppTouchscreenKeyboardKeysAmount=4 # Number of virtual keyboard keys that support autofire (currently 2 is maximum) AppTouchscreenKeyboardKeysAmountAutoFire=0 # Redefine on-screen keyboard keys to SDL keysyms - 6 keyboard keys + 4 multitouch gestures (zoom in/out and rotate left/right) RedefinedKeysScreenKb="WORLD_93 WORLD_94 WORLD_95 F10 5 6 7 8 9 0" # Names for on-screen keyboard keys, such as Fire, Jump, Run etc, separated by spaces, they are used in SDL config menu RedefinedKeysScreenKbNames="Joystick-button-1 Joystick-button-2 Console F10 5 6 7 8 9 0" # On-screen keys theme # 0 = Ultimate Droid by Sean Stieber (green, with gamepad joystick) # 1 = Simple Theme by Beholder (white, with gamepad joystick) # 2 = Sun by Sirea (yellow, with round joystick) # 3 = Keen by Gerstrong (multicolor, with round joystick) TouchscreenKeysTheme=2 # Redefine gamepad keys to SDL keysyms, button order is: # A B X Y L1 R1 L2 R2 LThumb RThumb RedefinedKeysGamepad="WORLD_93 WORLD_94 WORLD_95 F10 5 6 7 8 9 0" # How long to show startup menu button, in msec, 0 to disable startup menu StartupMenuButtonTimeout=3000 # Menu items to hide from startup menu, available menu items: # SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout HiddenMenuOptions='SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout' # Menu items to show at startup - this is Java code snippet, leave empty for default # new SettingsMenuMisc.ShowReadme(), (AppUsesMouse \&\& \! ForceRelativeMouseMode \? new SettingsMenuMouse.DisplaySizeConfig(true) : new SettingsMenu.DummyMenu()), new SettingsMenuMisc.OptionalDownloadConfig(true), new SettingsMenuMisc.GyroscopeCalibration() # Available menu items: # SettingsMenu.OkButton SettingsMenu.DummyMenu SettingsMenu.MainMenu SettingsMenuMisc.DownloadConfig SettingsMenuMisc.OptionalDownloadConfig SettingsMenuMisc.AudioConfig SettingsMenuMisc.VideoSettingsConfig SettingsMenuMisc.ShowReadme SettingsMenuMisc.GyroscopeCalibration SettingsMenuMisc.ResetToDefaultsConfig SettingsMenuMouse.MouseConfigMainMenu SettingsMenuMouse.DisplaySizeConfig SettingsMenuMouse.LeftClickConfig SettingsMenuMouse.RightClickConfig SettingsMenuMouse.AdditionalMouseConfig SettingsMenuMouse.JoystickMouseConfig SettingsMenuMouse.TouchPressureMeasurementTool SettingsMenuMouse.CalibrateTouchscreenMenu SettingsMenuKeyboard.KeyboardConfigMainMenu SettingsMenuKeyboard.ScreenKeyboardSizeConfig SettingsMenuKeyboard.ScreenKeyboardDrawSizeConfig SettingsMenuKeyboard.ScreenKeyboardThemeConfig SettingsMenuKeyboard.ScreenKeyboardTransparencyConfig SettingsMenuKeyboard.RemapHwKeysConfig SettingsMenuKeyboard.RemapScreenKbConfig SettingsMenuKeyboard.ScreenGesturesConfig SettingsMenuKeyboard.CustomizeScreenKbLayout #FirstStartMenuOptions='new SettingsMenu.DummyMenu(), new SettingsMenuMisc.OptionalDownloadConfig(true)' FirstStartMenuOptions='SettingsMenu.DummyMenu SettingsMenuMisc.OptionalDownloadConfig' # Enable multi-ABI binary, with hardware FPU support - it will also work on old devices, # but .apk size is 2x bigger (y) / (n) / (x86) / (all) MultiABI=n # Minimum amount of RAM application requires, in Mb, SDL will print warning to user if it's lower AppMinimumRAM=50 # Application version code (integer) AppVersionCode=VERSION_CODE_PLACEHOLDER # Application user-visible version name (string) AppVersionName="VERSION_NAME_PLACEHOLDER" # Reset SDL config when updating application to the new version (y) / (n) ResetSdlConfigForThisVersion=y # Delete application data files when upgrading (specify file/dir paths separated by spaces) DeleteFilesOnUpgrade="libsdl-DownloadFinished-0.flag openmsx_system" # Optional shared libraries to compile - removing some of them will save space # MP3 support by libMAD is encumbered by patents and libMAD is GPL-ed # Available libraries: mad (GPL-ed!) sdl_mixer sdl_image sdl_ttf sdl_net sdl_blitpool sdl_gfx sdl_sound intl xml2 lua jpeg png ogg flac tremor vorbis freetype xerces curl theora fluidsynth lzma lzo2 mikmod openal timidity zzip bzip2 yaml-cpp python boost_date_time boost_filesystem boost_iostreams boost_program_options boost_regex boost_signals boost_system boost_thread glu avcodec avdevice avfilter avformat avresample avutil swscale swresample bzip2 CompiledLibraries="freetype sdl_ttf png xml2 ogg vorbis theora tcl8.5" # Application uses custom build script AndroidBuild.sh instead of Android.mk (y) or (n) CustomBuildScript=y # Aditional CFLAGS for application AppCflags='-frtti -fexceptions' # Additional LDFLAGS for application AppLdflags='' # If application has headers with the same name as system headers, this option tries to fix compiler flags to make it compilable AppOverlapsSystemHeaders= # Build only following subdirs (empty will build all dirs, ignored with custom script) AppSubdirsBuild='' # Exclude these files from build AppBuildExclude='' # Application command line parameters, including app name as 0-th param AppCmdline='' # Here you may type readme text, which will be shown during startup. Format is: # Text in English, use \\\\\\\\n to separate lines^de:Text in Deutsch^ru:Text in Russian, and so on (that's four backslashes, nice isn't it?) ReadmeText='^Readme text' # Screen size is used by Google Play to prevent an app to be installed on devices with smaller screens # Minimum screen size that application supports: (s)mall / (m)edium / (l)arge MinimumScreenSize=s # Your AdMob Publisher ID, (n) if you don't want advertisements AdmobPublisherId=n # Your AdMob test device ID, to receive a test ad AdmobTestDeviceId= # Your AdMob banner size (BANNER/IAB_BANNER/IAB_LEADERBOARD/IAB_MRECT/IAB_WIDE_SKYSCRAPER/SMART_BANNER) AdmobBannerSize= openmsx-0.10.0/build/android/openmsx/AndroidBuild.sh0000755000175000017500000001150512262345041023155 0ustar manuelmanuel00000000000000#!/bin/bash #set -xv # TODO: find out if flavour can be passed from SDL build environment #openmsx_flavour="android-debug" openmsx_flavour="android" echo "AB:INFO Starting AndroidBuild.sh, #params: $#, params: $*" echo "AB:INFO pwd: $(pwd)" # Read environment.props (if it exists) to get following two params: # sdl_android_port_path # my_home_dir if [ ! -f environment.props ]; then echo "AB:ERROR: No file environment.props in $(pwd)" exit 1 fi . ./environment.props # Remember location of the current directory, which is the directory # with all android specific code for the app my_app_android_dir="$(pwd)" # Use latest version of the setEnvironment script; it is the one that uses GCC 4.6 set_sdl_app_environment="${sdl_android_port_path}/project/jni/application/setEnvironment.sh" # Parsing the CPU architecture information CPU_ARCH="$1" if [ "${CPU_ARCH}" = "armeabi" ]; then so_file=libapplication.so openmsx_target_cpu=arm else echo "AB:ERROR Unsupported architecture: $1" exit 1 fi #echo "AB:INFO current shell: ${SHELL}" #echo "AB:INFO BEGIN all environment params:" #set #echo "AB:INFO END all environment params:" #cd ${my_home_dir} # Unset make related environment parameters that get set by the # SDL for Android build system and that conflict with openMSX # build system unset BUILD_NUM_CPUS unset MAKEFLAGS unset MAKELEVEL unset MAKEOVERRIDES unset MFLAGS unset V cpu_count=1 if [ -f /proc/cpuinfo ]; then cpu_count=$(grep "processor[[:space:]]*:" /proc/cpuinfo | wc -l) if [ ${cpu_count} -eq 0 ]; then cpu_count=1 fi fi echo "AB:INFO Detected ${cpu_count} CPUs for parallel build" echo "AB:INFO Making this app for CPU architecture ${CPU_ARCH}" export SDL_ANDROID_PORT_PATH="${sdl_android_port_path}" export CXXFLAGS='-frtti -fexceptions -marm' export LDFLAGS='-lpng' unset BUILD_EXECUTABLE if [ $openmsx_flavour = "android" ]; then CXX_FLAGS_FILTER="sed -e 's/\\-mthumb//'" elif [ $openmsx_flavour = "android-debug" ]; then CXX_FLAGS_FILTER="sed -e 's/\\-mthumb//' -e 's/\\-DNDEBUG//g'" else echo "AB:ERROR Unknown openmsx_flavour: $openmsx_flavour" fi echo "AB:DEBUG CXX_FLAGS_FILTER: $CXX_FLAGS_FILTER" #"${set_sdl_app_environment}" /bin/bash -c "set" "${set_sdl_app_environment}" /bin/bash -c "\ echo \"AB:INFO entering openMSX home directory: ${my_home_dir}\"; \ cd ${my_home_dir};\ echo \"AB:INFO CXX: \${CXX}\";\ echo \"AB:INFO CXXFLAGS: \${CXXFLAGS}\";\ export _CC=\${CC};\ export _LD=\${LD};\ export ANDROID_LDFLAGS=\${LDFLAGS};\ export ANDROID_CXXFLAGS=\$(echo \${CXXFLAGS} | $CXX_FLAGS_FILTER);\ echo \"AB:INFO ANDROID_CXXFLAGS: \${ANDROID_CXXFLAGS}\";\ unset CXXFLAGS;\ make -j ${cpu_count} all\ OPENMSX_TARGET_CPU=${openmsx_target_cpu}\ OPENMSX_TARGET_OS=android\ OPENMSX_FLAVOUR=${openmsx_flavour}\ " if [ $? -ne 0 ]; then echo "AB:ERROR Make failed" exit 1 fi # Return to the directory containing this script and all data about this application # that the SDL APK build system requires cd "${my_app_android_dir}" # Copy the shared library overhere echo "AB:INFO Copying output file into android directory $(pwd)" cp "${my_home_dir}/derived/${openmsx_target_cpu}-android-${openmsx_flavour}/lib/openmsx.so" "${so_file}" if [ $? -ne 0 ]; then echo "AB:ERROR Copy failed" fi echo "AB:INFO Done with build of app" echo "AB:INFO Copying icon file" openmsx_icon_file="${my_home_dir}/share/icons/openMSX-logo-128.png" cp -p "${openmsx_icon_file}" icon.png echo "AB:INFO Validating if appdata.zip must be rebuild" if [ ! -f AndroidData/appdata.zip ]; then newfiles=1 else newfiles=$(find ${my_home_dir}/share ${my_home_dir}/Contrib/cbios ${my_app_android_dir}/AndroidBuild.sh -newer AndroidData/appdata.zip | wc -l) fi if [ ${newfiles} -gt 0 ]; then echo "AB:INFO Rebuilding appdata.zip" rm -f AndroidData/appdata.zip rm -rf AndroidData/appdata mkdir -p AndroidData/appdata/openmsx_system cd "${my_home_dir}"/share tar -c --exclude-vcs -f - . | ( cd "${my_app_android_dir}"/AndroidData/appdata/openmsx_system ; tar xf - ) cd "${my_home_dir}"/Contrib/cbios tar -c --exclude-vcs -f - . | ( cd "${my_app_android_dir}"/AndroidData/appdata/openmsx_system/machines ; tar xf - ) cd "${my_app_android_dir}"/AndroidData/appdata zip -r ../appdata.zip * > /dev/null cd .. rm -rf appdata echo "AB:INFO Done rebuilding appdata.zip" else echo "AB/INFO appdata.zip is still fine" fi MANIFEST="${sdl_android_port_path}/project/AndroidManifest.xml" # Patch manifest file to target android 2.3 and older so that the # virtual menu button will be rendered by newer Android versions sed -i "s^android:targetSdkVersion=\"[0-9]*\"^android:targetSdkVersion=\"10\"^" ${MANIFEST} # Remove network permissions from manifest. OpenMSX does not need it sed -i "s/<\/uses-permission>/<\!-- -->/" ${MANIFEST} exit 0 openmsx-0.10.0/build/android/openmsx/launch_anddev_build.sh0000755000175000017500000000073412262345041024571 0ustar manuelmanuel00000000000000#!/bin/bash echo "LAB:INFO Starting launch_anddev_build.sh" # Read environment.props (if it exists) to get following two params: # sdl_android_port_path # my_home_dir if [ ! -f environment.props ]; then echo "LAB:ERROR: No file environment.props in $(pwd)" exit 1 fi . ./environment.props ./generate_AndroidAppSettings.sh if [ $? -ne 0 ]; then exit 1 fi echo "LAB:INFO launching commandergenius build file" cd "${sdl_android_port_path}" ./build.sh $* exit $? openmsx-0.10.0/build/android/openmsx/AndroidAppSettings.cfg.template.170000644000175000017500000000336712262345041026550 0ustar manuelmanuel00000000000000# The application settings for Android libSDL port AppSettingVersion=17 LibSdlVersion=1.2 AppName="openMSX" AppFullName=org.openmsx.android.openmsx ScreenOrientation=h InhibitSuspend=y AppDataDownloadUrl="Application data|appdata.zip" VideoDepthBpp=16 NeedDepthBuffer=n NeedStencilBuffer=n NeedGles2=n SwVideoMode=y SdlVideoResize=y SdlVideoResizeKeepAspect=y CompatibilityHacks=n CompatibilityHacksStaticInit=n CompatibilityHacksTextInputEmulatesHwKeyboard=y CompatibilityHacksPreventAudioChopping=n CompatibilityHacksAppIgnoresAudioBufferSize=n AppUsesMouse=y AppNeedsTwoButtonMouse=n ShowMouseCursor=n ForceRelativeMouseMode=n AppNeedsArrowKeys=y AppNeedsTextInput=y AppUsesJoystick=y AppUsesAccelerometer=n AppUsesMultitouch=n NonBlockingSwapBuffers=y RedefinedKeys="SPACE RETURN NO_REMAP NO_REMAP MENU WORLD_92 NO_REMAP" AppTouchscreenKeyboardKeysAmount=4 AppTouchscreenKeyboardKeysAmountAutoFire=0 RedefinedKeysScreenKb="WORLD_93 WORLD_94 WORLD_95 F10 5 6 7 8 9 0" StartupMenuButtonTimeout=3000 HiddenMenuOptions='MouseConfigMainMenu AdditionalInputConfig AccelerometerConfig LeftClickConfig RightClickConfig AdditionalMouseConfig TouchPressureMeasurementTool ScreenGesturesConfig CustomizeScreenKbLayout' FirstStartMenuOptions='new Settings.DummyMenu(), new Settings.OptionalDownloadConfig(true)' MultiABI=n AppMinimumRAM=50 AppVersionCode=VERSION_CODE_PLACEHOLDER AppVersionName=VERSION_NAME_PLACEHOLDER ResetSdlConfigForThisVersion=y DeleteFilesOnUpgrade="libsdl-DownloadFinished-0.flag openmsx_system" CompiledLibraries="freetype sdl_ttf png xml2 ogg vorbis theora tcl8.5" CustomBuildScript=y AppCflags='-frtti -fexceptions' AppLdflags='' AppSubdirsBuild='' AppCmdline='' ReadmeText='^Readme text' MinimumScreenSize=s AdmobPublisherId=n AdmobTestDeviceId= AdmobBannerSize= openmsx-0.10.0/build/android/setup_anddev.sh0000755000175000017500000001274012262345041021607 0ustar manuelmanuel00000000000000#!/bin/bash function explain_usage() { echo "This script configures the \"commandergenius\" SDL Android port" echo "for the build of openMSX." echo "" echo "The script depends on the Android SDK and NDK and on the" echo "\"commandergenius\" SDL Android port." echo "" echo "First download and install the Android SDK and NDK" echo "as per the instructions on the Android website." echo "" echo "Please make sure to add the Android SDK and NDK tools locations" echo "to your PATH variable, as per the instructions on the Android" echo "website. Otherwise the setup will fail." echo "" echo "Subsequently download the \"commandergenius\" SDL Android port" echo "into your favorite location." echo "" echo "Example:" echo "> cd /opt" echo "> git clone https://github.com/pelya/commandergenius.git" echo "" echo "Once that is done, you can launch this script." echo "You must specify the path to the \"commandergenius\" SDL Android port" echo "on the command line or in environment parameter SDL_ANDROID_PORT_PATH." echo "" echo "Example 1:" echo "> export SDL_ANDROID_PORT_PATH=/opt/commandergenius" echo "> ./setup_anddev.sh" echo "" echo "Example 2:" echo "> ./setup_anddev.sh /opt/commandergenius" echo "" } if [ $# -eq 0 -a -z "$SDL_ANDROID_PORT_PATH" ]; then explain_usage exit 1 fi if [ $# -eq 1 ]; then sdl_android_port_path="$1" else sdl_android_port_path="$SDL_ANDROID_PORT_PATH" fi if [ ! -d "${sdl_android_port_path}" ]; then echo "Can not find directory ${sdl_android_port_path}" exit 1 fi sdl_port_app_dir="${sdl_android_port_path}/project/jni/application" if [ ! -d "${sdl_port_app_dir}" ]; then echo "Can not find expected sub-directory project/jni/application" echo "in specified directory ${sdl_android_port_path}" exit 1 fi this_script_dir=$(dirname $0) this_script_dir=$(cd ${this_script_dir}; pwd) my_home_dir=${this_script_dir%/*} my_home_dir=${my_home_dir%/*} my_android_dir="${my_home_dir}/build/android/openmsx" my_relative_dir="${my_android_dir##*/}" tcl_build=8.5.11 tcl_version=${tcl_build%.*} tcl_archive="${my_home_dir}/derived/3rdparty/download/tcl${tcl_build}-src.tar.gz" tcl_sdl_dir="${sdl_android_port_path}/project/jni/tcl${tcl_version}" if [ -d "${tcl_sdl_dir}" ]; then if [ ! -f "${tcl_sdl_dir}/${tcl_build}.txt" ]; then echo "ERROR: expecting TCL build ${tcl_build} in ${tcl_sdl_dir}" exit 1 fi fi if [ ! -d "${tcl_sdl_dir}" ]; then if [ ! -f "${tcl_archive}" ]; then cd "${my_home_dir}" echo "Downloading TCL" python build/android_download.py if [ $? -ne 0 ]; then echo "ERROR: Failed to download TCL" exit 1 fi if [ ! -f "${tcl_archive}" ]; then echo "ERROR: Failed to download TCL ${tcl_build}" exit 1 fi fi cd "${sdl_android_port_path}/project/jni" tar xzf "${tcl_archive}" tcl_relative_dir=tcl${tcl_version} mv tcl${tcl_build} ${tcl_relative_dir} touch ${tcl_relative_dir}/${tcl_build}.txt cp -p "${my_home_dir}/build/android/tcl${tcl_build}_Android.mk" ${tcl_relative_dir}/Android.mk cd ${tcl_relative_dir} mkdir -p lib/armeabi mkdir -p lib/armeabi-v7a mkdir -p include cp -p generic/*.h include cd unix if [ ! -f Makefile.in.original ]; then cp -p Makefile.in Makefile.in.original patch -i "${my_home_dir}/build/android/tcl${tcl_build}_unix_Makefile.in.patch" fi export BUILD_EXECUTABLE=yes ../../setCrossEnvironment.sh ./configure --host=arm-eabi make clean ../../setCrossEnvironment.sh make mv libtcl8.5.so ../lib/armeabi ../../setCrossEnvironment.sh ./configure --host=arm-eabi-v7a make clean ../../setCrossEnvironment.sh make mv libtcl8.5.so ../lib/armeabi-v7a fi echo "Setting-up softlink to this application in the SDL android port." if [ -h "${sdl_port_app_dir}/${my_relative_dir}" ]; then rm "${sdl_port_app_dir}/${my_relative_dir}" fi if [ -d "${sdl_port_app_dir}/${my_relative_dir}" ]; then echo "ERROR: found directory ${my_relative_dir} in ${sdl_port_app_dir}" echo "This seems to be another app with the same name." echo "Please resolve the conflict and then re-run this script." exit 1 fi ln -s "${my_android_dir}" "${sdl_port_app_dir}/${my_relative_dir}" rm -f "${sdl_port_app_dir}/src" ln -s "${my_relative_dir}" "${sdl_port_app_dir}/src" echo "Making environment.props file for the build script" cat > "${my_android_dir}/environment.props" << @EOT # Do not edit this file. It is generated by setup_anddev.sh sdl_android_port_path="${sdl_android_port_path}" my_home_dir="${my_home_dir}" @EOT cd "${my_android_dir}" ./generate_AndroidAppSettings.sh if [ $? -ne 0 ]; then exit 1 fi echo "Configuring SDL android port to build this application" cd "${sdl_android_port_path}" ./changeAppSettings.sh -a if [ $? -ne 0 ]; then echo "ERROR: an unexpected problem occurred while running changeAppSettings.sh -a" echo " in ${sdl_android_port_path}" exit 1 fi android update project -p project -t android-19 if [ $? -ne 0 ]; then echo "ERROR: an unexpected problem occurred while running \"android update ...\"" exit 1 fi echo "" echo "You can now build the application from the openMSX android build directory" echo "" echo "Example" echo "> cd ${my_android_dir}" echo "> ./launch_anddev_build.sh" echo "" echo "Note: the first time that you run the launch_anddev_build.sh script, you may get some error" echo " message. It is due to a subtle bug in the SDL android port." echo " Simply re-run the build.sh script a second time and it will work fine." openmsx-0.10.0/build/extract.py0000644000175000017500000000713412262345041017174 0ustar manuelmanuel00000000000000# Extract files from archives. from os import O_CREAT, O_WRONLY, fdopen, mkdir, open as osopen, utime try: from os import O_BINARY except ImportError: # Platforms that do not define O_BINARY do not need it either. O_BINARY = 0 from os.path import abspath, isdir, join as joinpath, sep, split as splitpath from stat import S_IRWXU, S_IRWXG, S_IRWXO, S_IXUSR, S_IXGRP, S_IXOTH import sys import tarfile from detectsys import detectOS hostOS = detectOS() # Note: Larger buffers might make extraction slower. bufSize = 16384 def extract(archivePath, destDir, rename = None): '''Extract the given archive to the given directory. If a rename function is given, it is called with the output path relative to the destination directory; the value returned by the rename function is used as the actual relative destination file path. This function sets file ownership and permissions like is done in newly created files and ignores the ownership and permissions from the archive, since we are not restoring a backup. ''' absDestDir = abspath(destDir) + sep if not isdir(absDestDir): raise ValueError( 'Destination directory "%s" does not exist' % absDestDir ) tar = tarfile.open(archivePath) # Note: According to the Python 2.6 docs, errorlevel can be passed as a # keyword argument to the open() call, but on Python 2.5 this does # not work. tar.errorlevel = 2 try: for member in tar.getmembers(): absMemberPath = abspath(joinpath(absDestDir, member.name)) if member.isdir(): absMemberPath += sep if not absMemberPath.startswith(absDestDir): raise ValueError( 'Refusing to extract tar entry "%s" ' 'outside destination directory' % member.name ) if rename: absMemberPath = absDestDir + rename( absMemberPath[len(absDestDir) : ] ) if member.isfile(): mode = S_IRWXU | S_IRWXG | S_IRWXO if not (member.mode & S_IXUSR): mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH) out = fdopen( osopen(absMemberPath, O_CREAT | O_WRONLY | O_BINARY, mode), 'wb' ) try: inp = tar.extractfile(member) bytesLeft = member.size while bytesLeft > 0: buf = inp.read(bufSize) out.write(buf) bytesLeft -= len(buf) buf = None finally: out.close() elif member.isdir(): if not isdir(absMemberPath): mkdir(absMemberPath) else: raise ValueError( 'Cannot extract tar entry "%s": ' 'not a regular file or a directory' % member.name ) # Set file/directory modification time to match the archive. # For example autotools track dependencies between archived files # and will attempt to regenerate them if the time stamps indicate # one is older than the other. # Note: Apparently Python 2.5's utime() cannot set timestamps on # directories in Windows. if member.isfile() or hostOS != 'mingw32': utime(absMemberPath, (member.mtime, member.mtime)) finally: tar.close() class TopLevelDirRenamer(object): def __init__(self, newName): self.newName = newName def __call__(self, oldPath): head, tail = splitpath(oldPath) headParts = head.split(sep) if not headParts: raise ValueError( 'Directory part is empty for entry "%s"' % oldPath ) headParts[0] = self.newName return sep.join(headParts + [ tail ]) if __name__ == '__main__': if 3 <= len(sys.argv) <= 4: if len(sys.argv) == 4: renameTopLevelDir = TopLevelDirRenamer(sys.argv[3]) else: renameTopLevelDir = None extract(sys.argv[1], sys.argv[2], renameTopLevelDir) else: print >> sys.stderr, \ 'Usage: python extract.py archive destination [new-top-level-dir]' sys.exit(2) openmsx-0.10.0/build/detectsys.py0000644000175000017500000000666612262345041017542 0ustar manuelmanuel00000000000000# Detect the native CPU and OS. # Actually we rely on the Python "platform" module and map its output to names # that the openMSX build understands. from platform import architecture, machine, system from subprocess import PIPE, STDOUT, Popen import sys def detectCPU(): '''Detects the CPU family (not the CPU model) of the machine were are running on. Raises ValueError if no known CPU is detected. ''' cpu = machine().lower() dashIndex = cpu.find('-') if dashIndex != -1: # Hurd returns "cputype-cpusubtype" instead of just "cputype". cpu = cpu[ : dashIndex] if cpu in ('x86_64', 'amd64'): return 'x86_64' elif cpu in ('x86', 'i386', 'i486', 'i586', 'i686'): return 'x86' elif cpu.startswith('ppc') or cpu.endswith('ppc') or cpu.startswith('power'): return 'ppc64' if cpu.endswith('64') else 'ppc' elif cpu.startswith('arm'): return 'arm' elif cpu.startswith('mips') or cpu == 'sgi': return 'mipsel' if cpu.endswith('el') else 'mips' elif cpu == 'm68k': return 'm68k' elif cpu == 'ia64': return 'ia64' elif cpu.startswith('alpha'): return 'alpha' elif cpu.startswith('hppa') or cpu.startswith('parisc'): return 'hppa' elif cpu.startswith('s390'): return 's390' elif cpu.startswith('sparc') or cpu.startswith('sun4u'): return 'sparc' elif cpu.startswith('sh'): return 'sheb' if cpu.endswith('eb') else 'sh' elif cpu == 'avr32': return 'avr32' elif cpu == '': # Python couldn't figure it out. os = system().lower() if os == 'windows': # Relatively safe bet. return 'x86' raise ValueError('Unable to detect CPU') else: raise ValueError('Unsupported or unrecognised CPU "%s"' % cpu) def detectOS(): '''Detects the operating system of the machine were are running on. Raises ValueError if no known OS is detected. ''' os = system().lower() if os in ( 'linux', 'darwin', 'freebsd', 'netbsd', 'openbsd', 'dragonfly', 'gnu' ): return os elif os.startswith('gnu/'): # GNU userland on non-Hurd kernel, for example Debian GNU/kFreeBSD. # For openMSX the kernel is not really relevant, so treat it like # a generic GNU system. return 'gnu' elif os.startswith('mingw') or os == 'windows': return 'mingw32' elif os == 'sunos': return 'solaris' elif os == '': # Python couldn't figure it out. raise ValueError('Unable to detect OS') else: raise ValueError('Unsupported or unrecognised OS "%s"' % os) def detectMaemo5(): try: proc = Popen( ["pkg-config", "--silence-errors", "--modversion", "maemo-version"], stdin = None, stdout = PIPE, stderr = None); except OSError: return False stdoutdata, stderrdata = proc.communicate() return proc.returncode == 0 and stdoutdata.startswith("5.0") if __name__ == '__main__': try: hostCPU = detectCPU() hostOS = detectOS() if hostOS == 'mingw32' and hostCPU == 'x86_64': # It is possible to run MinGW on 64-bit Windows, but producing # 64-bit code is not supported yet. hostCPU = 'x86' elif hostOS == 'darwin' and hostCPU == 'x86': # If Python is 64-bit, both the CPU and OS support it, so we can # compile openMSX for x86-64. Compiling in 32-bit mode might seem # safer, but will fail if using MacPorts on a 64-bit capable system. if architecture()[0] == '64bit': hostCPU = 'x86_64' elif hostOS == 'linux' and hostCPU == 'arm': # Detect maemo5 environment, e.g. Nokia N900 if detectMaemo5(): hostOS = 'maemo5' print '%s-%s' % (hostCPU, hostOS) except ValueError, ex: print >> sys.stderr, ex sys.exit(1) openmsx-0.10.0/build/platform-dingux.mk0000644000175000017500000000124412262345041020615 0ustar manuelmanuel00000000000000# Configuration for Dingux: Linux for Dingoo A320. # Set CXX before including platform-linux.mk (see comments in platform-linux.mk) ifeq ($(OPENMSX_TARGET_CPU),mipsel) # Automatically select the cross compiler from its default location. ifeq ($(origin CXX),default) CXX:=/opt/a320-toolchain/usr/bin/mipsel-linux-g++ endif # Use MIPS32 instruction set. TARGET_FLAGS+=-march=mips32 endif # Dingux is a Linux/uClibc system. include build/platform-linux.mk # Allow GMenu to identify our binary as an executable. # Since the file system is FAT this cannot be done with a permission flag. EXEEXT:=.dge LIBRARYEXT:=.so # Build a minimal set of components. LINK_MODE:=3RD_STA_MIN openmsx-0.10.0/build/3rdparty/0000755000175000017500000000000012262345041016713 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/3rdparty/libxml.vcxproj.filters0000644000175000017500000001607212262345041023274 0ustar manuelmanuel00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files openmsx-0.10.0/build/3rdparty/SDL_ttf-2.0.11.diff0000644000175000017500000000113512262345041021561 0ustar manuelmanuel00000000000000diff -ru SDL_ttf-2.0.11.org/Makefile.in SDL_ttf-2.0.11/Makefile.in --- SDL_ttf-2.0.11.org/Makefile.in 2012-01-15 05:44:08.000000000 +0100 +++ SDL_ttf-2.0.11/Makefile.in 2012-08-05 22:55:28.000000000 +0200 @@ -353,7 +353,7 @@ rm -f "$${dir}/so_locations"; \ done libSDL_ttf.la: $(libSDL_ttf_la_OBJECTS) $(libSDL_ttf_la_DEPENDENCIES) - $(libSDL_ttf_la_LINK) -rpath $(libdir) $(libSDL_ttf_la_OBJECTS) $(libSDL_ttf_la_LIBADD) $(LIBS) + $(libSDL_ttf_la_LINK) -rpath $(libdir) $(libSDL_ttf_la_OBJECTS) $(libSDL_ttf_la_LIBADD) clean-noinstPROGRAMS: @list='$(noinst_PROGRAMS)'; for p in $$list; do \ openmsx-0.10.0/build/3rdparty/glew.vcxproj.filters0000644000175000017500000000245412262345041022742 0ustar manuelmanuel00000000000000 {16b0a29f-ab69-458e-9125-149ccd95d72c} cpp;c;cxx;rc;def;r;odl;idl;hpj;bat {86b057dd-7908-43d0-b8ff-6e7630b2d0b1} h;hpp;hxx;hm;inl {1fc2b525-9c62-4fc0-8055-27e54fe40b7f} Source Files Header Files Header Files Resources openmsx-0.10.0/build/3rdparty/node.mk0000644000175000017500000000016012262345041020166 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ *.diff *.sln *.py *.vcxproj *.filters *.props include build/node-end.mk openmsx-0.10.0/build/3rdparty/libxml.vcxproj0000644000175000017500000004412512262345041021625 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {459F9609-4CE3-461B-A087-DE03F5140D9D} libxml StaticLibrary MultiByte StaticLibrary MultiByte true StaticLibrary MultiByte StaticLibrary MultiByte StaticLibrary MultiByte true StaticLibrary MultiByte <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibXml)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibXml)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibXml)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibXml)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibXml)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibXml)\ Running configure.js... pushd $(ThirdPartySrcDir)\$(LibNameLibXml)\win32 cscript configure.js iconv=no zlib=no popd Disabled $(ThirdPartySrcDir)\$(LibNameLibXml)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_REENTRANT;HAVE_COMPILER_TLS;HAVE_WIN32_THREADS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 ProgramDatabase MachineX86 Running configure.js... pushd $(ThirdPartySrcDir)\$(LibNameLibXml)\win32 cscript configure.js iconv=no zlib=no popd X64 Disabled $(ThirdPartySrcDir)\$(LibNameLibXml)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_REENTRANT;HAVE_COMPILER_TLS;HAVE_WIN32_THREADS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 ProgramDatabase MachineX64 Running configure.js... pushd $(ThirdPartySrcDir)\$(LibNameLibXml)\win32 cscript configure.js iconv=no zlib=no popd Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameLibXml)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_REENTRANT;HAVE_COMPILER_TLS;HAVE_WIN32_THREADS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 ProgramDatabase /LTCG %(AdditionalOptions) MachineX86 Running configure.js... pushd $(ThirdPartySrcDir)\$(LibNameLibXml)\win32 cscript configure.js iconv=no zlib=no popd X64 Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameLibXml)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_REENTRANT;HAVE_COMPILER_TLS;HAVE_WIN32_THREADS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 ProgramDatabase /LTCG %(AdditionalOptions) MachineX64 Running configure.js... pushd $(ThirdPartySrcDir)\$(LibNameLibXml)\win32 cscript configure.js iconv=no zlib=no popd Disabled $(ThirdPartySrcDir)\$(LibNameLibXml)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_REENTRANT;HAVE_COMPILER_TLS;HAVE_WIN32_THREADS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 ProgramDatabase MachineX86 Running configure.js... pushd $(ThirdPartySrcDir)\$(LibNameLibXml)\win32 cscript configure.js iconv=no zlib=no popd X64 Disabled $(ThirdPartySrcDir)\$(LibNameLibXml)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;_REENTRANT;HAVE_COMPILER_TLS;HAVE_WIN32_THREADS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 ProgramDatabase MachineX64 openmsx-0.10.0/build/3rdparty/libtheora.vcxproj.filters0000644000175000017500000002002312262345041023745 0ustar manuelmanuel00000000000000 {83741e6a-3d10-476a-b1ec-e9098fabacb7} {7f957e1c-7cef-42de-858b-962247fff00b} {e0aa57f5-2050-46a2-b36c-2a9e6ac323ff} {ba0ac045-9ac6-442e-9b34-8573540c5e6e} {8c1d2b59-d884-4c59-8ae2-6f33fc26187e} {e8759675-802a-4da8-b9e9-c845f9952a12} {965f7fba-63ea-4828-8dac-2ee27d82acd8} include\theora include\theora include\theora lib lib lib\enc lib\enc lib\enc lib\enc lib\enc lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc lib\enc\x86_vc lib\enc\x86_vc lib\enc\x86_vc lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec lib\dec\x86_vc lib\dec\x86_vc lib\dec\x86_vc lib\dec\x86_vc openmsx-0.10.0/build/3rdparty/zlib-1.2.8.diff0000644000175000017500000000121112262345041021144 0ustar manuelmanuel00000000000000diff -ru zlib-1.2.8.orig/configure zlib-1.2.8/configure --- zlib-1.2.8.orig/configure 2013-03-24 06:30:09.000000000 +0100 +++ zlib-1.2.8/configure 2013-05-18 18:22:58.000000000 +0200 @@ -192,9 +192,9 @@ EXE='.exe' ;; MINGW* | mingw*) # temporary bypass - rm -f $test.[co] $test $test$shared_ext - echo "Please use win32/Makefile.gcc instead." | tee -a configure.log - leave 1 +# rm -f $test.[co] $test $test$shared_ext +# echo "Please use win32/Makefile.gcc instead." | tee -a configure.log +# leave 1 LDSHARED=${LDSHARED-"$cc -shared"} LDSHAREDLIBC="" EXE='.exe' ;; openmsx-0.10.0/build/3rdparty/zlib.vcxproj0000644000175000017500000010627612262345041021304 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 LIB ASM Debug Win32 LIB ASM Debug x64 LIB ASM Release Win32 LIB ASM Release x64 Release Win32 Release x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5} zlib StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameZlib)\ Disabled WIN32;_DEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL .\Win32_LIB_ASM_Debug/zlib.pch .\Win32_LIB_ASM_Debug/ .\Win32_LIB_ASM_Debug/ .\Win32_LIB_ASM_Debug/ Level3 true EditAndContinue _DEBUG;%(PreprocessorDefinitions) 0x0409 Win32_LIB_ASM_Debug\zlibd.lib true true .\Win32_LIB_ASM_Debug/zlib.bsc Disabled WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true EditAndContinue _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX86 true .\Win32_LIB_Debug/zlib.bsc Full AnySuitable true Size true true true WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true NDEBUG;%(PreprocessorDefinitions) 0x0409 /LTCG %(AdditionalOptions) true MachineX86 true .\Win32_LIB_Release/zlib.bsc MaxSpeed OnlyExplicitInline WIN32;NDEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true MultiThreadedDLL true .\Win32_LIB_ASM_Release/zlib.pch .\Win32_LIB_ASM_Release/ .\Win32_LIB_ASM_Release/ .\Win32_LIB_ASM_Release/ Level3 true NDEBUG;%(PreprocessorDefinitions) 0x0409 .\Win32_LIB_ASM_Release\zlib.lib true true .\Win32_LIB_ASM_Release/zlib.bsc Disabled WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX86 true .\Win32_LIB_Debug/zlib.bsc X64 Disabled WIN32;_DEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL .\Win32_LIB_ASM_Debug/zlib.pch .\Win32_LIB_ASM_Debug/ .\Win32_LIB_ASM_Debug/ .\Win32_LIB_ASM_Debug/ Level3 true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0409 Win32_LIB_ASM_Debug\zlibd.lib true true .\Win32_LIB_ASM_Debug/zlib.bsc X64 Disabled WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX64 true .\Win32_LIB_Debug/zlib.bsc X64 Full AnySuitable true Size true true true WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true NDEBUG;%(PreprocessorDefinitions) 0x0409 /LTCG %(AdditionalOptions) true MachineX64 true .\Win32_LIB_Release/zlib.bsc X64 MaxSpeed OnlyExplicitInline WIN32;NDEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true MultiThreadedDLL true .\Win32_LIB_ASM_Release/zlib.pch .\Win32_LIB_ASM_Release/ .\Win32_LIB_ASM_Release/ .\Win32_LIB_ASM_Release/ Level3 true NDEBUG;%(PreprocessorDefinitions) 0x0409 .\Win32_LIB_ASM_Release\zlib.lib true true .\Win32_LIB_ASM_Release/zlib.bsc X64 Disabled WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX64 true .\Win32_LIB_Debug/zlib.bsc %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) true true true true %(PreprocessorDefinitions) \Scratch\Build\openmsx\external\zlib-1.2.3\win32;%(AdditionalIncludeDirectories) %(PreprocessorDefinitions) \Scratch\Build\openmsx\external\zlib-1.2.3\win32;%(AdditionalIncludeDirectories) %(PreprocessorDefinitions) \Scratch\Build\openmsx\external\zlib-1.2.3\win32;%(AdditionalIncludeDirectories) %(PreprocessorDefinitions) \Scratch\Build\openmsx\external\zlib-1.2.3\win32;%(AdditionalIncludeDirectories) openmsx-0.10.0/build/3rdparty/tcl.vcxproj.filters0000644000175000017500000005773212262345041022577 0ustar manuelmanuel00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files openmsx-0.10.0/build/3rdparty/libtheora.vcxproj0000644000175000017500000005767412262345041022324 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {653F3841-3F26-49B9-AFCF-091DB4B67031} libtheora Win32Proj StaticLibrary Unicode StaticLibrary Unicode true StaticLibrary Unicode StaticLibrary Unicode StaticLibrary Unicode true StaticLibrary Unicode <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTheora)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTheora)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTheora)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTheora)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTheora)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTheora)\ Disabled $(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_BIND_TO_CURRENT_CRT_VERSION;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBTHEORA_EXPORTS;DEBUG;OC_X86_ASM;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level3 ProgramDatabase MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_BIND_TO_CURRENT_CRT_VERSION;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBTHEORA_EXPORTS;DEBUG;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level3 ProgramDatabase MachineX64 Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_BIND_TO_CURRENT_CRT_VERSION;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBTHEORA_EXPORTS;OC_X86_ASM;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level4 CompileAsC 4244;4267;4057;4100;4245;%(DisableSpecificWarnings) MachineX86 X64 Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_BIND_TO_CURRENT_CRT_VERSION;WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBTHEORA_EXPORTS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level4 CompileAsC 4244;4267;4057;4100;4245;%(DisableSpecificWarnings) MachineX64 Disabled $(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_BIND_TO_CURRENT_CRT_VERSION;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBTHEORA_EXPORTS;DEBUG;OC_X86_ASM;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 ProgramDatabase MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameTheora)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_BIND_TO_CURRENT_CRT_VERSION;WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBTHEORA_EXPORTS;DEBUG;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level3 ProgramDatabase MachineX64 true true true true true true $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc $(IntDir)%(Filename)1.obj $(IntDir)%(Filename)1.xdc openmsx-0.10.0/build/3rdparty/SDL-1.2.15.diff0000644000175000017500000000435612262345041020721 0ustar manuelmanuel00000000000000diff -ru SDL-1.2.15.org/src/video/quartz/SDL_QuartzEvents.m SDL-1.2.15/src/video/quartz/SDL_QuartzEvents.m --- SDL-1.2.15.org/src/video/quartz/SDL_QuartzEvents.m 2012-01-19 07:30:06.000000000 +0100 +++ SDL-1.2.15/src/video/quartz/SDL_QuartzEvents.m 2012-01-29 14:50:43.000000000 +0100 @@ -345,7 +345,9 @@ the scancode/keysym. */ if (SDL_TranslateUNICODE && state == SDL_PRESSED) { - [field_edit interpretKeyEvents:[NSArray arrayWithObject:event]]; + if (!([event modifierFlags] & NSCommandKeyMask)) { + [field_edit interpretKeyEvents:[NSArray arrayWithObject:event]]; + } chars = [ event characters ]; numChars = [ chars length ]; if (numChars > 0) @@ -383,7 +385,7 @@ } } - if (SDL_getenv ("SDL_ENABLEAPPEVENTS")) + if (SDL_getenv ("SDL_ENABLEAPPEVENTS") && !(mode_flags & SDL_FULLSCREEN)) [ NSApp sendEvent:event ]; } diff -ru SDL-1.2.15.org/src/video/quartz/SDL_QuartzWindow.h SDL-1.2.15/src/video/quartz/SDL_QuartzWindow.h --- SDL-1.2.15.org/src/video/quartz/SDL_QuartzWindow.h 2012-01-19 07:30:06.000000000 +0100 +++ SDL-1.2.15/src/video/quartz/SDL_QuartzWindow.h 2012-01-29 14:53:02.000000000 +0100 @@ -38,6 +38,7 @@ - (void)appWillUnhide:(NSNotification*)note; - (void)appDidUnhide:(NSNotification*)note; - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag; +- (BOOL)performKeyEquivalent:(NSEvent*)event; @end /* Delegate for our NSWindow to send SDLQuit() on close */ diff -ru SDL-1.2.15.org/src/video/quartz/SDL_QuartzWindow.m SDL-1.2.15/src/video/quartz/SDL_QuartzWindow.m --- SDL-1.2.15.org/src/video/quartz/SDL_QuartzWindow.m 2012-01-19 07:30:06.000000000 +0100 +++ SDL-1.2.15/src/video/quartz/SDL_QuartzWindow.m 2012-01-29 14:54:35.000000000 +0100 @@ -197,6 +197,14 @@ return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]; } +- (BOOL)performKeyEquivalent:(NSEvent*)event +{ + /* give the menu a chance to handle the key equivalent */ + [[NSApp mainMenu] performKeyEquivalent:event]; + /* avoid beep by pretending we handled it */ + return YES; +} + @end @implementation SDL_QuartzWindowDelegate openmsx-0.10.0/build/3rdparty/3rdparty.sln0000644000175000017500000006111212262345041021202 0ustar manuelmanuel00000000000000 Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glew", "glew.vcxproj", "{E78E1412-E9F8-475B-9504-72031C8FBAEA}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetype", "freetype.vcxproj", "{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libogg", "libogg.vcxproj", "{15CBFEFF-7965-41F5-B4E2-21E8795C9159}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "libpng.vcxproj", "{0008960E-E0DD-41A6-8265-00B31DDB4C21}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtheora", "libtheora.vcxproj", "{653F3841-3F26-49B9-AFCF-091DB4B67031}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvorbis", "libvorbis.vcxproj", "{3A214E06-B95E-4D61-A291-1F8DF2EC10FD}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libxml", "libxml.vcxproj", "{459F9609-4CE3-461B-A087-DE03F5140D9D}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL", "SDL.vcxproj", "{81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDL_ttf", "SDL_ttf.vcxproj", "{DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SDLmain", "SDLmain.vcxproj", "{DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tcl", "tcl.vcxproj", "{CCCEA506-D026-4915-BEE0-374F65D7C764}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib.vcxproj", "{05A68CD4-A282-4475-B9F7-ED3C9A0109B5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Developer|Win32 = Developer|Win32 Developer|x64 = Developer|x64 LIB ASM Debug|Win32 = LIB ASM Debug|Win32 LIB ASM Debug|x64 = LIB ASM Debug|x64 LIB ASM Release|Win32 = LIB ASM Release|Win32 LIB ASM Release|x64 = LIB ASM Release|x64 Release_STDIO|Win32 = Release_STDIO|Win32 Release_STDIO|x64 = Release_STDIO|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Debug|Win32.ActiveCfg = Debug|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Debug|Win32.Build.0 = Debug|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Debug|x64.ActiveCfg = Debug|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Debug|x64.Build.0 = Debug|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Developer|Win32.ActiveCfg = Developer|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Developer|Win32.Build.0 = Developer|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Developer|x64.ActiveCfg = Developer|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Developer|x64.Build.0 = Developer|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Debug|x64.Build.0 = Debug|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Release|Win32.Build.0 = Release|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Release|x64.ActiveCfg = Release|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.LIB ASM Release|x64.Build.0 = Release|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Release_STDIO|Win32.ActiveCfg = Release|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Release_STDIO|x64.ActiveCfg = Release|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Release_STDIO|x64.Build.0 = Release|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Release|Win32.ActiveCfg = Release|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Release|Win32.Build.0 = Release|Win32 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Release|x64.ActiveCfg = Release|x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA}.Release|x64.Build.0 = Release|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|Win32.ActiveCfg = Debug|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|Win32.Build.0 = Debug|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|x64.ActiveCfg = Debug|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|x64.Build.0 = Debug|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Developer|Win32.ActiveCfg = Developer|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Developer|Win32.Build.0 = Developer|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Developer|x64.ActiveCfg = Developer|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Developer|x64.Build.0 = Developer|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Debug|x64.Build.0 = Debug|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Release|Win32.Build.0 = Release|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Release|x64.ActiveCfg = Release|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.LIB ASM Release|x64.Build.0 = Release|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release_STDIO|Win32.ActiveCfg = Release|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release_STDIO|x64.ActiveCfg = Release|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release_STDIO|x64.Build.0 = Release|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|Win32.ActiveCfg = Release|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|Win32.Build.0 = Release|Win32 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|x64.ActiveCfg = Release|x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|x64.Build.0 = Release|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Debug|Win32.ActiveCfg = Debug|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Debug|Win32.Build.0 = Debug|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Debug|x64.ActiveCfg = Debug|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Debug|x64.Build.0 = Debug|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Developer|Win32.ActiveCfg = Developer|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Developer|Win32.Build.0 = Developer|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Developer|x64.ActiveCfg = Developer|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Developer|x64.Build.0 = Developer|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Debug|x64.Build.0 = Debug|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Release|Win32.Build.0 = Release|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Release|x64.ActiveCfg = Release|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.LIB ASM Release|x64.Build.0 = Release|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Release_STDIO|Win32.ActiveCfg = Release|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Release_STDIO|x64.ActiveCfg = Release|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Release_STDIO|x64.Build.0 = Release|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Release|Win32.ActiveCfg = Release|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Release|Win32.Build.0 = Release|Win32 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Release|x64.ActiveCfg = Release|x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159}.Release|x64.Build.0 = Release|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Debug|Win32.ActiveCfg = Debug|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Debug|Win32.Build.0 = Debug|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Debug|x64.ActiveCfg = Debug|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Debug|x64.Build.0 = Debug|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Developer|Win32.ActiveCfg = Developer|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Developer|Win32.Build.0 = Developer|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Developer|x64.ActiveCfg = Developer|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Developer|x64.Build.0 = Developer|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Debug|x64.Build.0 = Debug|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Release|Win32.Build.0 = Release|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Release|x64.ActiveCfg = Release|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.LIB ASM Release|x64.Build.0 = Release|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Release_STDIO|Win32.ActiveCfg = Release|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Release_STDIO|x64.ActiveCfg = Release|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Release_STDIO|x64.Build.0 = Release|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Release|Win32.ActiveCfg = Release|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Release|Win32.Build.0 = Release|Win32 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Release|x64.ActiveCfg = Release|x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21}.Release|x64.Build.0 = Release|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Debug|Win32.ActiveCfg = Debug|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Debug|Win32.Build.0 = Debug|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Debug|x64.ActiveCfg = Debug|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Debug|x64.Build.0 = Debug|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Developer|Win32.ActiveCfg = Developer|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Developer|Win32.Build.0 = Developer|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Developer|x64.ActiveCfg = Developer|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Developer|x64.Build.0 = Developer|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Debug|x64.Build.0 = Debug|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Release|Win32.Build.0 = Release|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Release|x64.ActiveCfg = Release|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.LIB ASM Release|x64.Build.0 = Release|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Release_STDIO|Win32.ActiveCfg = Release|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Release_STDIO|x64.ActiveCfg = Release|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Release_STDIO|x64.Build.0 = Release|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Release|Win32.ActiveCfg = Release|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Release|Win32.Build.0 = Release|Win32 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Release|x64.ActiveCfg = Release|x64 {653F3841-3F26-49B9-AFCF-091DB4B67031}.Release|x64.Build.0 = Release|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Debug|Win32.ActiveCfg = Debug|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Debug|Win32.Build.0 = Debug|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Debug|x64.ActiveCfg = Debug|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Debug|x64.Build.0 = Debug|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Developer|Win32.ActiveCfg = Developer|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Developer|Win32.Build.0 = Developer|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Developer|x64.ActiveCfg = Developer|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Developer|x64.Build.0 = Developer|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Debug|x64.Build.0 = Debug|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Release|Win32.Build.0 = Release|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Release|x64.ActiveCfg = Release|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.LIB ASM Release|x64.Build.0 = Release|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Release_STDIO|Win32.ActiveCfg = Release|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Release_STDIO|x64.ActiveCfg = Release|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Release_STDIO|x64.Build.0 = Release|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Release|Win32.ActiveCfg = Release|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Release|Win32.Build.0 = Release|Win32 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Release|x64.ActiveCfg = Release|x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD}.Release|x64.Build.0 = Release|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Debug|Win32.ActiveCfg = Debug|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Debug|Win32.Build.0 = Debug|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Debug|x64.ActiveCfg = Debug|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Debug|x64.Build.0 = Debug|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Developer|Win32.ActiveCfg = Developer|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Developer|Win32.Build.0 = Developer|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Developer|x64.ActiveCfg = Developer|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Developer|x64.Build.0 = Developer|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Debug|x64.Build.0 = Debug|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Release|Win32.Build.0 = Release|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Release|x64.ActiveCfg = Release|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.LIB ASM Release|x64.Build.0 = Release|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Release_STDIO|Win32.ActiveCfg = Release|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Release_STDIO|x64.ActiveCfg = Release|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Release_STDIO|x64.Build.0 = Release|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Release|Win32.ActiveCfg = Release|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Release|Win32.Build.0 = Release|Win32 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Release|x64.ActiveCfg = Release|x64 {459F9609-4CE3-461B-A087-DE03F5140D9D}.Release|x64.Build.0 = Release|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Win32.ActiveCfg = Debug|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|Win32.Build.0 = Debug|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|x64.ActiveCfg = Debug|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Debug|x64.Build.0 = Debug|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Developer|Win32.ActiveCfg = Developer|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Developer|Win32.Build.0 = Developer|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Developer|x64.ActiveCfg = Developer|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Developer|x64.Build.0 = Developer|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Debug|x64.Build.0 = Debug|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Release|Win32.Build.0 = Release|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Release|x64.ActiveCfg = Release|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.LIB ASM Release|x64.Build.0 = Release|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release_STDIO|Win32.ActiveCfg = Release|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release_STDIO|x64.ActiveCfg = Release|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release_STDIO|x64.Build.0 = Release|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Win32.ActiveCfg = Release|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|Win32.Build.0 = Release|Win32 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|x64.ActiveCfg = Release|x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68}.Release|x64.Build.0 = Release|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Debug|Win32.ActiveCfg = Debug|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Debug|Win32.Build.0 = Debug|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Debug|x64.ActiveCfg = Debug|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Debug|x64.Build.0 = Debug|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Developer|Win32.ActiveCfg = Developer|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Developer|Win32.Build.0 = Developer|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Developer|x64.ActiveCfg = Developer|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Developer|x64.Build.0 = Developer|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Debug|x64.Build.0 = Debug|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Release|Win32.Build.0 = Release|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Release|x64.ActiveCfg = Release|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.LIB ASM Release|x64.Build.0 = Release|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Release_STDIO|Win32.ActiveCfg = Release|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Release_STDIO|x64.ActiveCfg = Release|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Release_STDIO|x64.Build.0 = Release|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Release|Win32.ActiveCfg = Release|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Release|Win32.Build.0 = Release|Win32 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Release|x64.ActiveCfg = Release|x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255}.Release|x64.Build.0 = Release|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|Win32.ActiveCfg = Debug|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|Win32.Build.0 = Debug|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|x64.ActiveCfg = Debug|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Debug|x64.Build.0 = Debug|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Developer|Win32.ActiveCfg = Developer|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Developer|Win32.Build.0 = Developer|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Developer|x64.ActiveCfg = Developer|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Developer|x64.Build.0 = Developer|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Debug|x64.Build.0 = Debug|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Release|Win32.Build.0 = Release|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Release|x64.ActiveCfg = Release|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.LIB ASM Release|x64.Build.0 = Release|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release_STDIO|Win32.ActiveCfg = Release_STDIO|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release_STDIO|Win32.Build.0 = Release_STDIO|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release_STDIO|x64.ActiveCfg = Release_STDIO|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release_STDIO|x64.Build.0 = Release_STDIO|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|Win32.ActiveCfg = Release|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|Win32.Build.0 = Release|Win32 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|x64.ActiveCfg = Release|x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A}.Release|x64.Build.0 = Release|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Debug|Win32.ActiveCfg = Debug|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Debug|Win32.Build.0 = Debug|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Debug|x64.ActiveCfg = Debug|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Debug|x64.Build.0 = Debug|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Developer|Win32.ActiveCfg = Developer|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Developer|Win32.Build.0 = Developer|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Developer|x64.ActiveCfg = Developer|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Developer|x64.Build.0 = Developer|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Debug|Win32.ActiveCfg = Debug|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Debug|Win32.Build.0 = Debug|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Debug|x64.ActiveCfg = Debug|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Debug|x64.Build.0 = Debug|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Release|Win32.ActiveCfg = Release|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Release|Win32.Build.0 = Release|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Release|x64.ActiveCfg = Release|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.LIB ASM Release|x64.Build.0 = Release|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Release_STDIO|Win32.ActiveCfg = Release|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Release_STDIO|x64.ActiveCfg = Release|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Release_STDIO|x64.Build.0 = Release|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Release|Win32.ActiveCfg = Release|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Release|Win32.Build.0 = Release|Win32 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Release|x64.ActiveCfg = Release|x64 {CCCEA506-D026-4915-BEE0-374F65D7C764}.Release|x64.Build.0 = Release|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Debug|Win32.ActiveCfg = Debug|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Debug|Win32.Build.0 = Debug|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Debug|x64.ActiveCfg = Debug|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Debug|x64.Build.0 = Debug|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Developer|Win32.ActiveCfg = Developer|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Developer|Win32.Build.0 = Developer|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Developer|x64.ActiveCfg = Developer|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Developer|x64.Build.0 = Developer|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Debug|Win32.ActiveCfg = LIB ASM Debug|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Debug|Win32.Build.0 = LIB ASM Debug|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Debug|x64.ActiveCfg = LIB ASM Debug|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Debug|x64.Build.0 = LIB ASM Debug|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Release|Win32.ActiveCfg = LIB ASM Release|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Release|Win32.Build.0 = LIB ASM Release|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Release|x64.ActiveCfg = LIB ASM Release|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.LIB ASM Release|x64.Build.0 = LIB ASM Release|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Release_STDIO|Win32.ActiveCfg = Release|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Release_STDIO|x64.ActiveCfg = Release|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Release_STDIO|x64.Build.0 = Release|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Release|Win32.ActiveCfg = Release|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Release|Win32.Build.0 = Release|Win32 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Release|x64.ActiveCfg = Release|x64 {05A68CD4-A282-4475-B9F7-ED3C9A0109B5}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal openmsx-0.10.0/build/3rdparty/libogg.vcxproj.filters0000644000175000017500000000256112262345041023246 0ustar manuelmanuel00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx Source Files Source Files Header Files Header Files openmsx-0.10.0/build/3rdparty/glew-1.9.0.diff0000644000175000017500000001370612262345041021155 0ustar manuelmanuel00000000000000diff -ru glew-1.9.0.orig/build/glew.rc glew-1.9.0/build/glew.rc --- glew-1.9.0.orig/build/glew.rc 2012-08-06 17:59:08.000000000 +0200 +++ glew-1.9.0/build/glew.rc 2013-05-25 21:44:27.000000000 +0200 @@ -56,7 +56,7 @@ BEGIN BLOCK "040904b0" BEGIN - VALUE "Comments", "The OpenGL Extension Wrangler Library\r\nCopyright (C) 2002-2008, Milan Ikits \r\nCopyright (C) 2002-2008, Marcelo E. Magallon \r\nCopyright (C) 2002, Lev Povalahev\r\nAll rights reserved.\r\n\r\nRedistribution and use in source and binary forms, with or without \r\nmodification, are permitted provided that the following conditions are met:\r\n\r\n* Redistributions of source code must retain the above copyright notice, \r\n this list of conditions and the following disclaimer.\r\n* Redistributions in binary form must reproduce the above copyright notice, \r\n this list of conditions and the following disclaimer in the documentation \r\n and/or other materials provided with the distribution.\r\n* The name of the author may be used to endorse or promote products \r\n derived from this software without specific prior written permission.\r\n\r\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' \r\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \r\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r\nARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE \r\nLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR \r\nCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF \r\nSUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r\nINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r\nCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF\r\nTHE POSSIBILITY OF SUCH DAMAGE.\r\n\r\n\r\nMesa 3-D graphics library\r\n\r\nVersion: 7.0\r\n\r\nCopyright (C) 1999-2007 Brian Paul All Rights Reserved.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a\r\ncopy of this software and associated documentation files (the ''Software''),\r\nto deal in the Software without restriction, including without limitation\r\nthe rights to use, copy, modify, merge, publish, distribute, sublicense,\r\nand/or sell copies of the Software, and to permit persons to whom the\r\nSoftware is furnished to do so, subject to the following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included\r\nin all copies or substantial portions of the Software.\r\n\r\nTHE SOFTWARE IS PROVIDED ''AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS\r\nOR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r\nBRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN\r\nAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n\r\n\r\nCopyright (c) 2007 The Khronos Group Inc.\r\n\r\nPermission is hereby granted, free of charge, to any person obtaining a\r\ncopy of this software and/or associated documentation files (the\r\n''Materials''), to deal in the Materials without restriction, including\r\nwithout limitation the rights to use, copy, modify, merge, publish,\r\ndistribute, sublicense, and/or sell copies of the Materials, and to\r\npermit persons to whom the Materials are furnished to do so, subject to\r\nthe following conditions:\r\n\r\nThe above copyright notice and this permission notice shall be included\r\nin all copies or substantial portions of the Materials.\r\n\r\nTHE MATERIALS ARE PROVIDED ''AS IS'', WITHOUT WARRANTY OF ANY KIND,\r\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r\nIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\r\nCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\r\nTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\r\nMATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.\0" + VALUE "Comments", "The description that was here fails to compile on Visual Studio, see https://sourceforge.net/p/glew/bugs/201/. It doesn't end up in any openMSX binary anyway.\0" VALUE "CompanyName", "\0" VALUE "FileDescription", "The OpenGL Extension Wrangler Library\0" VALUE "FileVersion", "1,9,0,0\0" diff -ru glew-1.9.0.orig/include/GL/glew.h glew-1.9.0/include/GL/glew.h --- glew-1.9.0.orig/include/GL/glew.h 2012-08-06 17:59:08.000000000 +0200 +++ glew-1.9.0/include/GL/glew.h 2013-05-25 21:39:27.000000000 +0200 @@ -113,7 +113,7 @@ #define GLEW_APIENTRY_DEFINED # if defined(__MINGW32__) || defined(__CYGWIN__) # define APIENTRY __stdcall -# elif (_MSC_VER >= 800) || defined(_STDCALL_SUPPORTED) || defined(__BORLANDC__) +# elif (defined(_MSC_VER) && (_MSC_VER >= 800)) || defined(_STDCALL_SUPPORTED) || defined(__BORLANDC__) # define APIENTRY __stdcall # else # define APIENTRY diff -ru glew-1.9.0.orig/Makefile glew-1.9.0/Makefile --- glew-1.9.0.orig/Makefile 2012-08-06 17:59:08.000000000 +0200 +++ glew-1.9.0/Makefile 2013-05-25 21:39:27.000000000 +0200 @@ -72,7 +72,9 @@ OPT = $(POPT) endif INCLUDE = -Iinclude -CFLAGS = $(OPT) $(WARN) $(INCLUDE) $(CFLAGS.EXTRA) +# openMSX dedicated build: use flavour's optimization flags instead: +#CFLAGS = $(OPT) $(WARN) $(INCLUDE) $(CFLAGS.EXTRA) +CFLAGS += $(WARN) $(INCLUDE) $(CFLAGS.EXTRA) all debug: glew.lib glew.lib.mx glew.bin @@ -262,7 +264,7 @@ install.bin: glew.bin $(INSTALL) -d -m 0755 $(BINDIR) - $(INSTALL) -s -m 0755 bin/$(GLEWINFO.BIN) bin/$(VISUALINFO.BIN) $(BINDIR)/ +# $(INSTALL) -s -m 0755 bin/$(GLEWINFO.BIN) bin/$(VISUALINFO.BIN) $(BINDIR)/ install.include: $(INSTALL) -d -m 0755 $(INCDIR) openmsx-0.10.0/build/3rdparty/libpng.vcxproj.filters0000644000175000017500000000645112262345041023260 0ustar manuelmanuel00000000000000 {cfb12311-2c25-406d-90fa-1a94e04b9107} cpp;c;cxx;rc;def;r;odl;idl;hpj;bat {dadb6a38-0397-4c12-958f-43fc35c69553} h;hpp;hxx;hm;inl {d19955fe-f136-45bb-baa2-ab0c5710c4f1} ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Resource Files openmsx-0.10.0/build/3rdparty/SDL_ttf.vcxproj0000644000175000017500000004635212262345041021641 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {DDDBD07D-DC76-4AF6-8D02-3E2DEB6EE255} SDL_ttf StaticLibrary false StaticLibrary false true StaticLibrary false StaticLibrary false StaticLibrary false true StaticLibrary false <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL_ttf)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL_ttf)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL_ttf)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL_ttf)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL_ttf)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL_ttf)\ _DEBUG;%(PreprocessorDefinitions) true true Win32 .\Debug/SDL_ttf.tlb Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true EditAndContinue _DEBUG;%(PreprocessorDefinitions) 0x0409 true .\Debug/SDL_ttf.bsc MachineX86 _DEBUG;%(PreprocessorDefinitions) true true X64 .\Debug/SDL_ttf.tlb Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0409 true .\Debug/SDL_ttf.bsc MachineX64 NDEBUG;%(PreprocessorDefinitions) true true Win32 .\Release/SDL_ttf.tlb Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true NDEBUG;%(PreprocessorDefinitions) 0x0409 true .\Release/SDL_ttf.bsc MachineX86 NDEBUG;%(PreprocessorDefinitions) true true X64 .\Release/SDL_ttf.tlb Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true NDEBUG;%(PreprocessorDefinitions) 0x0409 true .\Release/SDL_ttf.bsc MachineX64 _DEBUG;%(PreprocessorDefinitions) true true Win32 .\Debug/SDL_ttf.tlb Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0409 true .\Debug/SDL_ttf.bsc MachineX86 _DEBUG;%(PreprocessorDefinitions) true true X64 .\Debug/SDL_ttf.tlb Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;$(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;%(PreprocessorDefinitions) 0x0409 true .\Debug/SDL_ttf.bsc MachineX64 %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) openmsx-0.10.0/build/3rdparty/zlib.vcxproj.filters0000644000175000017500000001106212262345041022737 0ustar manuelmanuel00000000000000 {269dfb7b-8e1f-4ee4-9e90-6694ad6d215d} cpp;c;cxx;rc;def;r;odl;idl;hpj;bat {087dbb60-2787-47d2-9ae0-07f401f8e7a4} h;hpp;hxx;hm;inl {63bf4111-ea8c-486d-9d72-a9edb977d301} ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe {2e1192dc-b6bb-4833-96e9-7588f23bb7dc} asm;obj;c;cpp;cxx;h;hpp;hxx Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Assembler Files %28Unsupported%29 Assembler Files %28Unsupported%29 Resource Files Source Files openmsx-0.10.0/build/3rdparty/freetype.vcxproj.filters0000644000175000017500000001401412262345041023622 0ustar manuelmanuel00000000000000 {62c90e15-3afc-4349-8061-9d4644f7b19c} cpp;c;cxx;rc;def;r;odl;idl;hpj;bat {b30a8e21-f649-4e57-88c5-ea62fc84dd51} h;hpp;hxx;hm;inl Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files openmsx-0.10.0/build/3rdparty/SDL.vcxproj0000644000175000017500000006443312262345041020764 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {81CE8DAF-EBB2-4761-8E45-B71ABCCA8C68} SDL StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDL)\ _DEBUG;%(PreprocessorDefinitions) true true Win32 .\Debug/SDL.tlb Disabled $(DXSDK_DIR)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0400;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true EditAndContinue Default _DEBUG;%(PreprocessorDefinitions) 0x0409 MachineX86 _DEBUG;%(PreprocessorDefinitions) true true X64 .\Debug/SDL.tlb Disabled $(DXSDK_DIR)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0400;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true ProgramDatabase Default _DEBUG;%(PreprocessorDefinitions) 0x0409 MachineX64 NDEBUG;%(PreprocessorDefinitions) true true Win32 .\Release/SDL.tlb Full AnySuitable true Size true true true $(DXSDK_DIR)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;NDEBUG;_WINDOWS;_WIN32_WINNT=0x0400;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true Default NDEBUG;%(PreprocessorDefinitions) 0x0409 /LTCG %(AdditionalOptions) MachineX86 NDEBUG;%(PreprocessorDefinitions) true true X64 .\Release/SDL.tlb Full AnySuitable true Size true true true $(DXSDK_DIR)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;NDEBUG;_WINDOWS;_WIN32_WINNT=0x0400;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true Default NDEBUG;%(PreprocessorDefinitions) 0x0409 /LTCG %(AdditionalOptions) MachineX64 _DEBUG;%(PreprocessorDefinitions) true true Win32 .\Debug/SDL.tlb Disabled $(DXSDK_DIR)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0400;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase Default _DEBUG;%(PreprocessorDefinitions) 0x0409 MachineX86 _DEBUG;%(PreprocessorDefinitions) true true X64 .\Debug/SDL.tlb Disabled $(DXSDK_DIR)\include;$(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;_DEBUG;_WINDOWS;_WIN32_WINNT=0x0400;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase Default _DEBUG;%(PreprocessorDefinitions) 0x0409 MachineX64 openmsx-0.10.0/build/3rdparty/libvorbis.vcxproj0000644000175000017500000004520012262345041022324 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {3A214E06-B95E-4D61-A291-1F8DF2EC10FD} libvorbis Win32Proj StaticLibrary Unicode StaticLibrary Unicode true StaticLibrary Unicode StaticLibrary Unicode StaticLibrary Unicode true StaticLibrary Unicode <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameVorbis)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameVorbis)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameVorbis)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameVorbis)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameVorbis)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameVorbis)\ Disabled $(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBVORBIS_EXPORTS;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level4 EditAndContinue CompileAsC X64 Disabled $(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBVORBIS_EXPORTS;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level4 ProgramDatabase CompileAsC Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBVORBIS_EXPORTS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level4 ProgramDatabase CompileAsC 4244;4100;4267;4189;4305;4127;4706;%(DisableSpecificWarnings) X64 Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBVORBIS_EXPORTS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level4 ProgramDatabase CompileAsC 4244;4100;4267;4189;4305;4127;4706;%(DisableSpecificWarnings) Disabled $(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBVORBIS_EXPORTS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level4 EditAndContinue CompileAsC X64 Disabled $(ThirdPartySrcDir)\$(LibNameVorbis)\include;$(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBVORBIS_EXPORTS;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level4 ProgramDatabase CompileAsC openmsx-0.10.0/build/3rdparty/libogg.vcxproj0000644000175000017500000003253112262345041021577 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {15CBFEFF-7965-41F5-B4E2-21E8795C9159} libogg Win32Proj StaticLibrary Unicode StaticLibrary Unicode true StaticLibrary Unicode StaticLibrary Unicode StaticLibrary Unicode true StaticLibrary Unicode <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameOgg)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameOgg)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameOgg)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameOgg)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameOgg)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameOgg)\ Disabled $(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOGG_EXPORTS;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level4 EditAndContinue CompileAsC MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOGG_EXPORTS;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level4 ProgramDatabase CompileAsC MachineX64 Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOGG_EXPORTS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level4 CompileAsC 4244;%(DisableSpecificWarnings) MachineX86 X64 Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;LIBOGG_EXPORTS;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level4 CompileAsC 4244;%(DisableSpecificWarnings) MachineX64 Disabled $(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOGG_EXPORTS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level4 EditAndContinue CompileAsC MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameOgg)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;LIBOGG_EXPORTS;%(PreprocessorDefinitions) true Default MultiThreadedDebug Level4 ProgramDatabase CompileAsC MachineX64 openmsx-0.10.0/build/3rdparty/freetype-2.4.12.diff0000644000175000017500000002407712262345041022124 0ustar manuelmanuel00000000000000diff -ru freetype-2.4.12.orig/include/freetype/config/ftoption.h freetype-2.4.12/include/freetype/config/ftoption.h --- freetype-2.4.12.orig/include/freetype/config/ftoption.h 2013-01-22 14:31:11.000000000 +0100 +++ freetype-2.4.12/include/freetype/config/ftoption.h 2013-05-18 12:01:54.000000000 +0200 @@ -148,7 +148,7 @@ /* */ /* Define this macro if you want to enable this `feature'. */ /* */ -#define FT_CONFIG_OPTION_USE_LZW +/* #define FT_CONFIG_OPTION_USE_LZW */ /*************************************************************************/ @@ -163,7 +163,7 @@ /* Define this macro if you want to enable this `feature'. See also */ /* the macro FT_CONFIG_OPTION_SYSTEM_ZLIB below. */ /* */ -#define FT_CONFIG_OPTION_USE_ZLIB +/* #define FT_CONFIG_OPTION_USE_ZLIB */ /*************************************************************************/ @@ -296,7 +296,7 @@ /* able to synthesize a Unicode charmap out of the glyphs found in the */ /* fonts. */ /* */ -#define FT_CONFIG_OPTION_ADOBE_GLYPH_LIST +#undef FT_CONFIG_OPTION_ADOBE_GLYPH_LIST /*************************************************************************/ @@ -309,7 +309,7 @@ /* */ /* Note that the `FOND' resource isn't checked. */ /* */ -#define FT_CONFIG_OPTION_MAC_FONTS +/* #define FT_CONFIG_OPTION_MAC_FONTS */ /*************************************************************************/ @@ -487,7 +487,7 @@ /* embedded bitmaps in all formats using the SFNT module (namely */ /* TrueType & OpenType). */ /* */ -#define TT_CONFIG_OPTION_EMBEDDED_BITMAPS +/* #define TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ /*************************************************************************/ @@ -502,7 +502,7 @@ /* */ /* (By default, the module uses `PSNames' to extract glyph names.) */ /* */ -#define TT_CONFIG_OPTION_POSTSCRIPT_NAMES +#undef TT_CONFIG_OPTION_POSTSCRIPT_NAMES /*************************************************************************/ @@ -516,7 +516,7 @@ /* Accessing SFNT names is done through the functions declared in */ /* `freetype/ftsnames.h'. */ /* */ -#define TT_CONFIG_OPTION_SFNT_NAMES +/* #define TT_CONFIG_OPTION_SFNT_NAMES */ /*************************************************************************/ @@ -669,7 +669,7 @@ /* and avar tables). This has many similarities to Type 1 Multiple */ /* Masters support. */ /* */ -#define TT_CONFIG_OPTION_GX_VAR_SUPPORT +/* #define TT_CONFIG_OPTION_GX_VAR_SUPPORT */ /*************************************************************************/ @@ -677,7 +677,7 @@ /* Define TT_CONFIG_OPTION_BDF if you want to include support for */ /* an embedded `BDF ' table within SFNT-based bitmap formats. */ /* */ -#define TT_CONFIG_OPTION_BDF +/* #define TT_CONFIG_OPTION_BDF */ /*************************************************************************/ @@ -723,7 +723,7 @@ /* files into an existing face. Note that if set, the T1 driver will be */ /* unable to produce kerning distances. */ /* */ -#undef T1_CONFIG_OPTION_NO_AFM +#define T1_CONFIG_OPTION_NO_AFM /*************************************************************************/ diff -ru freetype-2.4.12.orig/modules.cfg freetype-2.4.12/modules.cfg --- freetype-2.4.12.orig/modules.cfg 2011-11-26 13:19:10.000000000 +0100 +++ freetype-2.4.12/modules.cfg 2013-05-18 12:06:58.000000000 +0200 @@ -37,35 +37,35 @@ # PostScript Type 1 font driver. # # This driver needs the `psaux', `pshinter', and `psnames' modules. -FONT_MODULES += type1 +#FONT_MODULES += type1 # CFF/OpenType font driver. # # This driver needs the `sfnt', `pshinter', and `psnames' modules. -FONT_MODULES += cff +#FONT_MODULES += cff # Type 1 CID-keyed font driver. # # This driver needs the `psaux', `pshinter', and `psnames' modules. -FONT_MODULES += cid +#FONT_MODULES += cid # PFR/TrueDoc font driver. See optional extension ftpfr.c below also. -FONT_MODULES += pfr +#FONT_MODULES += pfr # PostScript Type 42 font driver. # # This driver needs the `truetype' and `psaux' modules. -FONT_MODULES += type42 +#FONT_MODULES += type42 # Windows FONT/FNT font driver. See optional extension ftwinfnt.c below # also. -FONT_MODULES += winfonts +#FONT_MODULES += winfonts # PCF font driver. -FONT_MODULES += pcf +#FONT_MODULES += pcf # BDF font driver. See optional extension ftbdf.c below also. -FONT_MODULES += bdf +#FONT_MODULES += bdf # SFNT files support. If used without `truetype' or `cff', it supports # bitmap-only fonts within an SFNT wrapper. @@ -79,10 +79,10 @@ #### # FreeType's auto hinter. -HINTING_MODULES += autofit +#HINTING_MODULES += autofit # PostScript hinter. -HINTING_MODULES += pshinter +#HINTING_MODULES += pshinter # The TrueType hinting engine doesn't have a module of its own but is # controlled in file include/freetype/config/ftoption.h @@ -94,7 +94,7 @@ #### # Monochrome rasterizer. -RASTER_MODULES += raster +#RASTER_MODULES += raster # Anti-aliasing rasterizer. RASTER_MODULES += smooth @@ -107,7 +107,7 @@ # FreeType's cache sub-system (quite stable but still in beta -- this means # that its public API is subject to change if necessary). See # include/freetype/ftcache.h. Needs ftglyph.c. -AUX_MODULES += cache +#AUX_MODULES += cache # TrueType GX/AAT table validation. Needs ftgxval.c below. # AUX_MODULES += gxvalid @@ -115,12 +115,12 @@ # Support for streams compressed with gzip (files with suffix .gz). # # See include/freetype/ftgzip.h for the API. -AUX_MODULES += gzip +#AUX_MODULES += gzip # Support for streams compressed with LZW (files with suffix .Z). # # See include/freetype/ftlzw.h for the API. -AUX_MODULES += lzw +#AUX_MODULES += lzw # Support for streams compressed with bzip2 (files with suffix .bz2). # @@ -134,13 +134,13 @@ # Auxiliary PostScript driver component to share common code. # # This module depends on `psnames'. -AUX_MODULES += psaux +#AUX_MODULES += psaux # Support for PostScript glyph names. # # This module can be controlled in ftconfig.h # (FT_CONFIG_OPTION_POSTSCRIPT_NAMES). -AUX_MODULES += psnames +#AUX_MODULES += psnames #### @@ -150,12 +150,12 @@ # Exact bounding box calculation. # # See include/freetype/ftbbox.h for the API. -BASE_EXTENSIONS += ftbbox.c +#BASE_EXTENSIONS += ftbbox.c # Access BDF-specific strings. Needs BDF font driver. # # See include/freetype/ftbdf.h for the API. -BASE_EXTENSIONS += ftbdf.c +#BASE_EXTENSIONS += ftbdf.c # Utility functions for converting 1bpp, 2bpp, 4bpp, and 8bpp bitmaps into # 8bpp format, and for emboldening of bitmap glyphs. @@ -166,17 +166,17 @@ # Access CID font information. # # See include/freetype/ftcid.h for the API. -BASE_EXTENSIONS += ftcid.c +#BASE_EXTENSIONS += ftcid.c # Access FSType information. Needs fttype1.c. # # See include/freetype/freetype.h for the API. -BASE_EXTENSIONS += ftfstype.c +#BASE_EXTENSIONS += ftfstype.c # Support for GASP table queries. # # See include/freetype/ftgasp.h for the API. -BASE_EXTENSIONS += ftgasp.c +#BASE_EXTENSIONS += ftgasp.c # Convenience functions to handle glyphs. Needs ftbitmap.c. # @@ -186,32 +186,32 @@ # Interface for gxvalid module. # # See include/freetype/ftgxval.h for the API. -BASE_EXTENSIONS += ftgxval.c +#BASE_EXTENSIONS += ftgxval.c # Support for LCD color filtering of subpixel bitmaps. # # See include/freetype/ftlcdfil.h for the API. -BASE_EXTENSIONS += ftlcdfil.c +#BASE_EXTENSIONS += ftlcdfil.c # Multiple Master font interface. # # See include/freetype/ftmm.h for the API. -BASE_EXTENSIONS += ftmm.c +#BASE_EXTENSIONS += ftmm.c # Interface for otvalid module. # # See include/freetype/ftotval.h for the API. -BASE_EXTENSIONS += ftotval.c +#BASE_EXTENSIONS += ftotval.c # Support for FT_Face_CheckTrueTypePatents. # # See include/freetype/freetype.h for the API. -BASE_EXTENSIONS += ftpatent.c +#BASE_EXTENSIONS += ftpatent.c # Interface for accessing PFR-specific data. Needs PFR font driver. # # See include/freetype/ftpfr.h for the API. -BASE_EXTENSIONS += ftpfr.c +#BASE_EXTENSIONS += ftpfr.c # Path stroker. Needs ftglyph.c. # @@ -221,24 +221,24 @@ # Support for synthetic embolding and slanting of fonts. Needs ftbitmap.c. # # See include/freetype/ftsynth.h for the API. -BASE_EXTENSIONS += ftsynth.c +#BASE_EXTENSIONS += ftsynth.c # Interface to access data specific to PostScript Type 1 and Type 2 (CFF) # fonts. # # See include/freetype/t1tables.h for the API. -BASE_EXTENSIONS += fttype1.c +#BASE_EXTENSIONS += fttype1.c # Interface for accessing data specific to Windows FNT files. Needs winfnt # driver. # # See include/freetype/ftwinfnt.h for the API. -BASE_EXTENSIONS += ftwinfnt.c +#BASE_EXTENSIONS += ftwinfnt.c # Support functions for X11. # # See include/freetype/ftxf86.h for the API. -BASE_EXTENSIONS += ftxf86.c +#BASE_EXTENSIONS += ftxf86.c #### #### The components `ftsystem.c' (for memory allocation and stream I/O openmsx-0.10.0/build/3rdparty/unzip.py0000644000175000017500000000117412262345041020435 0ustar manuelmanuel00000000000000import sys import zipfile import os import os.path def UnzipFile(file, outputdir): if not os.path.exists(outputdir): os.mkdir(outputdir, 755) zip = zipfile.ZipFile(open(file, 'rb')) for name in zip.namelist(): output = os.path.join(outputdir, name) if name.endswith('/'): if not os.path.exists(output): os.mkdir(output) else: output = open(output, 'wb') output.write(zip.read(name)) output.close() if __name__ == '__main__': if len(sys.argv) == 3: UnzipFile(sys.argv[1], sys.argv[2]) else: print >> sys.stderr, \ 'Usage: python unzip.py zipfile outputdir' sys.exit(2) openmsx-0.10.0/build/3rdparty/3rdparty.props0000644000175000017500000001207012262345041021550 0ustar manuelmanuel00000000000000 ..\.. $(OpenMSXRootDir)\derived $(DerivedDir)\$(Platform)-VC-$(Configuration) $(BuildDir)\3rdParty $(DerivedDir)\3rdParty\src $(ThirdPartyBuildDir)\build $(ThirdPartyBuildDir)\install\lib $(OpenMSXRootDir)\src $(BuildDir)\build $(BuildDir)\install $(BuildDir)\config freetype-2.4.12 glew-1.9.0 libpng-1.2.50 libxml2-2.8.0 libogg-1.3.0 SDL-1.2.15 SDL_ttf-2.0.11 SDLmain tcl8.5.14 libtheora-1.1.1 libvorbis-1.3.3 zlib-1.2.8 <_ProjectFileVersion>10.0.30319.1 $(OpenMSXRootDir) true $(DerivedDir) true $(BuildDir) true $(ThirdPartyBuildDir) true $(ThirdPartySrcDir) true $(ThirdPartyIntDir) true $(ThirdPartyOutDir) true $(OpenMSXSrcDir) true $(OpenMSXIntDir) true $(OpenMSXOutDir) true $(OpenMSXConfigDir) true $(LibNameFreeType) true $(LibNameGlew) true $(LibNameLibPng) true $(LibNameLibXml) true $(LibNameOgg) true $(LibNameSDL) true $(LibNameSDL_ttf) true $(LibNameSDLmain) true $(LibNameTcl) true $(LibNameTheora) true $(LibNameVorbis) true $(LibNameZlib) true openmsx-0.10.0/build/3rdparty/tcl8.5.14.diff0000644000175000017500000000301612262345041021005 0ustar manuelmanuel00000000000000diff -ru tcl8.5.14.orig/generic/tcl.h tcl8.5.14/generic/tcl.h --- tcl8.5.14.orig/generic/tcl.h 2013-03-22 12:39:05.000000000 +0100 +++ tcl8.5.14/generic/tcl.h 2013-05-26 09:28:57.000000000 +0200 @@ -168,7 +168,7 @@ * MSVCRT. */ -#if (defined(__WIN32__) && (defined(_MSC_VER) || (__BORLANDC__ >= 0x0550) || defined(__LCC__) || defined(__WATCOMC__) || (defined(__GNUC__) && defined(__declspec)))) +#if (defined(__WIN32__) && (defined(_MSC_VER) || (defined(__BORLANDC__) && (__BORLANDC__ >= 0x0550)) || defined(__LCC__) || defined(__WATCOMC__) || (defined(__GNUC__) && defined(__declspec)))) # define HAVE_DECLSPEC 1 # ifdef STATIC_BUILD # define DLLIMPORT diff -ru tcl8.5.14.orig/unix/configure tcl8.5.14/unix/configure --- tcl8.5.14.orig/unix/configure 2013-04-01 20:36:35.000000000 +0200 +++ tcl8.5.14/unix/configure 2013-05-26 09:28:57.000000000 +0200 @@ -15068,7 +15068,7 @@ else if test "$cross_compiling" = yes; then - tcl_cv_strtod_buggy=buggy + tcl_cv_strtod_buggy=ok else cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ diff -ru tcl8.5.14.orig/win/Makefile.in tcl8.5.14/win/Makefile.in --- tcl8.5.14.orig/win/Makefile.in 2013-04-01 20:36:36.000000000 +0200 +++ tcl8.5.14/win/Makefile.in 2013-05-26 09:34:41.000000000 +0200 @@ -606,7 +606,7 @@ $(COPY) $(REG_LIB_FILE) $(LIB_INSTALL_DIR)/reg$(REGDOTVER); \ fi -install-libraries: libraries install-tzdata install-msgs +install-libraries: libraries @for i in $(prefix)/lib $(INCLUDE_INSTALL_DIR) \ $(SCRIPT_INSTALL_DIR); \ do \ openmsx-0.10.0/build/3rdparty/libpng.vcxproj0000644000175000017500000003657612262345041021624 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {0008960E-E0DD-41A6-8265-00B31DDB4C21} libpng StaticLibrary StaticLibrary StaticLibrary StaticLibrary StaticLibrary StaticLibrary <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibPng)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibPng)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibPng)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibPng)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibPng)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameLibPng)\ Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameZlib);%(AdditionalIncludeDirectories) WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true Sync MultiThreaded true true png.h Level3 Default /LTCG %(AdditionalOptions) MachineX86 X64 Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameZlib);%(AdditionalIncludeDirectories) WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true Sync MultiThreaded true true png.h Level3 Default /LTCG %(AdditionalOptions) MachineX64 Disabled $(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameZlib);%(AdditionalIncludeDirectories) WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug png.h Level3 EditAndContinue Default MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameZlib);%(AdditionalIncludeDirectories) WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug png.h Level3 ProgramDatabase Default MachineX64 Disabled $(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameZlib);%(AdditionalIncludeDirectories) WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug png.h Level3 ProgramDatabase Default MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameLibPng);$(ThirdPartySrcDir)\$(LibNameZlib);%(AdditionalIncludeDirectories) WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug png.h Level3 ProgramDatabase Default MachineX64 openmsx-0.10.0/build/3rdparty/SDLmain.vcxproj0000644000175000017500000004533512262345041021631 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release_STDIO Win32 Release_STDIO x64 Release Win32 Release x64 {DA956FD3-E142-46F2-9DD5-C78BEBB56B7A} SDLmain StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false StaticLibrary false <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameSDLmain)\ Full AnySuitable true true true true $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true MultiThreaded true Level3 true Default /LTCG %(AdditionalOptions) true X64 Full AnySuitable true true true true $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true MultiThreaded true Level3 true Default /LTCG %(AdditionalOptions) true Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;NDEBUG;_WINDOWS;NO_STDIO_REDIRECT;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true Default /LTCG %(AdditionalOptions) true MachineX86 X64 Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;NDEBUG;_WINDOWS;NO_STDIO_REDIRECT;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true Default /LTCG %(AdditionalOptions) true MachineX64 Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true ProgramDatabase Default true MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true ProgramDatabase Default true MachineX64 Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase Default true MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameSDL)\include;%(AdditionalIncludeDirectories) _CRT_SECURE_NO_DEPRECATE;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase Default true MachineX64 openmsx-0.10.0/build/3rdparty/tcl.vcxproj0000644000175000017500000010027712262345041021121 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {CCCEA506-D026-4915-BEE0-374F65D7C764} tcl Win32Proj StaticLibrary MultiByte StaticLibrary MultiByte true StaticLibrary MultiByte StaticLibrary MultiByte StaticLibrary MultiByte true StaticLibrary MultiByte <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTcl)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTcl)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTcl)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTcl)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTcl)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameTcl)\ Disabled $(ThirdPartySrcDir)\$(LibNameTcl)\win;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameTcl)\libtommath;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TCL_TOMMATH;MP_PREC=4;TCL_CFGVAL_ENCODING="cp1252";STDC_HEADERS;STATIC_BUILD;TCL_CFG_DEBUG;TCL_USE_STATIC_PACKAGES=1;BUILD_tcl;inline=__inline;TCL_PIPE_DLL="tclpip85sg.dll";CFG_INSTALL_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_INSTALL_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_INSTALL_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_INSTALL_INCDIR="C:\\Program Files\\Tcl\\include";CFG_INSTALL_DOCDIR="C:\\Program Files\\Tcl\\doc";CFG_RUNTIME_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_RUNTIME_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_RUNTIME_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_RUNTIME_INCDIR="C:\\Program Files\\Tcl\\include";CFG_RUNTIME_DOCDIR="C:\\Program Files\\Tcl\\doc";%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 EditAndContinue MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameTcl)\win;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameTcl)\libtommath;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TCL_TOMMATH;MP_PREC=4;TCL_CFGVAL_ENCODING="cp1252";STDC_HEADERS;STATIC_BUILD;TCL_CFG_DEBUG;TCL_USE_STATIC_PACKAGES=1;BUILD_tcl;inline=__inline;TCL_PIPE_DLL="tclpip85sg.dll";CFG_INSTALL_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_INSTALL_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_INSTALL_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_INSTALL_INCDIR="C:\\Program Files\\Tcl\\include";CFG_INSTALL_DOCDIR="C:\\Program Files\\Tcl\\doc";CFG_RUNTIME_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_RUNTIME_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_RUNTIME_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_RUNTIME_INCDIR="C:\\Program Files\\Tcl\\include";CFG_RUNTIME_DOCDIR="C:\\Program Files\\Tcl\\doc";_stati64=_stat64;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 ProgramDatabase MachineX64 /MP %(AdditionalOptions) Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameTcl)\win;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameTcl)\libtommath;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TCL_TOMMATH;MP_PREC=4;TCL_CFGVAL_ENCODING="cp1252";STDC_HEADERS;STATIC_BUILD;TCL_CFG_DEBUG;TCL_USE_STATIC_PACKAGES=1;BUILD_tcl;inline=__inline;TCL_PIPE_DLL="tclpip85sg.dll";CFG_INSTALL_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_INSTALL_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_INSTALL_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_INSTALL_INCDIR="C:\\Program Files\\Tcl\\include";CFG_INSTALL_DOCDIR="C:\\Program Files\\Tcl\\doc";CFG_RUNTIME_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_RUNTIME_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_RUNTIME_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_RUNTIME_INCDIR="C:\\Program Files\\Tcl\\include";CFG_RUNTIME_DOCDIR="C:\\Program Files\\Tcl\\doc";%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 ProgramDatabase MachineX86 X64 /MP %(AdditionalOptions) Full AnySuitable true Size true true $(ThirdPartySrcDir)\$(LibNameTcl)\win;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameTcl)\libtommath;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TCL_TOMMATH;MP_PREC=4;TCL_CFGVAL_ENCODING="cp1252";STDC_HEADERS;STATIC_BUILD;TCL_CFG_DEBUG;TCL_USE_STATIC_PACKAGES=1;BUILD_tcl;inline=__inline;TCL_PIPE_DLL="tclpip85sg.dll";CFG_INSTALL_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_INSTALL_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_INSTALL_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_INSTALL_INCDIR="C:\\Program Files\\Tcl\\include";CFG_INSTALL_DOCDIR="C:\\Program Files\\Tcl\\doc";CFG_RUNTIME_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_RUNTIME_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_RUNTIME_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_RUNTIME_INCDIR="C:\\Program Files\\Tcl\\include";CFG_RUNTIME_DOCDIR="C:\\Program Files\\Tcl\\doc";_stati64=_stat64;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 ProgramDatabase MachineX64 Disabled $(ThirdPartySrcDir)\$(LibNameTcl)\win;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameTcl)\libtommath;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TCL_TOMMATH;MP_PREC=4;TCL_CFGVAL_ENCODING="cp1252";STDC_HEADERS;STATIC_BUILD;TCL_CFG_DEBUG;TCL_USE_STATIC_PACKAGES=1;BUILD_tcl;inline=__inline;TCL_PIPE_DLL="tclpip85sg.dll";CFG_INSTALL_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_INSTALL_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_INSTALL_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_INSTALL_INCDIR="C:\\Program Files\\Tcl\\include";CFG_INSTALL_DOCDIR="C:\\Program Files\\Tcl\\doc";CFG_RUNTIME_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_RUNTIME_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_RUNTIME_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_RUNTIME_INCDIR="C:\\Program Files\\Tcl\\include";CFG_RUNTIME_DOCDIR="C:\\Program Files\\Tcl\\doc";%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 ProgramDatabase MachineX86 X64 Disabled $(ThirdPartySrcDir)\$(LibNameTcl)\win;$(ThirdPartySrcDir)\$(LibNameTcl)\generic;$(ThirdPartySrcDir)\$(LibNameTcl)\libtommath;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;TCL_TOMMATH;MP_PREC=4;TCL_CFGVAL_ENCODING="cp1252";STDC_HEADERS;STATIC_BUILD;TCL_CFG_DEBUG;TCL_USE_STATIC_PACKAGES=1;BUILD_tcl;inline=__inline;TCL_PIPE_DLL="tclpip85sg.dll";CFG_INSTALL_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_INSTALL_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_INSTALL_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_INSTALL_INCDIR="C:\\Program Files\\Tcl\\include";CFG_INSTALL_DOCDIR="C:\\Program Files\\Tcl\\doc";CFG_RUNTIME_LIBDIR="C:\\Program Files\\Tcl\\lib";CFG_RUNTIME_BINDIR="C:\\Program Files\\Tcl\\bin";CFG_RUNTIME_SCRDIR="C:\\Program Files\\Tcl\\lib\\tcl8.5";CFG_RUNTIME_INCDIR="C:\\Program Files\\Tcl\\include";CFG_RUNTIME_DOCDIR="C:\\Program Files\\Tcl\\doc";_stati64=_stat64;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 ProgramDatabase MachineX64 openmsx-0.10.0/build/3rdparty/glew.vcxproj0000644000175000017500000004544112262345041021276 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {E78E1412-E9F8-475B-9504-72031C8FBAEA} glew_static StaticLibrary false MultiByte StaticLibrary false MultiByte true StaticLibrary false MultiByte StaticLibrary false MultiByte StaticLibrary false MultiByte true StaticLibrary false MultiByte <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameGlew)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameGlew)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameGlew)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameGlew)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameGlew)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameGlew)\ Disabled $(ThirdPartySrcDir)\$(LibNameGlew)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;GLEW_STATIC;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;GLEW_MX;GLEW_STATIC;%(PreprocessorDefinitions) 0x0409 true MachineX86 true .\glew/lib/glew_static.bsc X64 Disabled $(ThirdPartySrcDir)\$(LibNameGlew)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;GLEW_STATIC;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;GLEW_MX;GLEW_STATIC;%(PreprocessorDefinitions) 0x0409 true MachineX64 true .\glew/lib/glew_static.bsc Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameGlew)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;GLEW_STATIC;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true NDEBUG;GLEW_MX;GLEW_STATIC;%(PreprocessorDefinitions) 0x0409 true MachineX86 true .\glew/lib/glew_static.bsc X64 Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameGlew)\include;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;GLEW_STATIC;%(PreprocessorDefinitions) true Sync MultiThreaded true true Level3 true NDEBUG;GLEW_MX;GLEW_STATIC;%(PreprocessorDefinitions) 0x0409 true MachineX64 true .\glew/lib/glew_static.bsc Disabled $(ThirdPartySrcDir)\$(LibNameGlew)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;GLEW_STATIC;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;GLEW_MX;GLEW_STATIC;%(PreprocessorDefinitions) 0x0409 true MachineX86 true .\glew/lib/glew_static.bsc X64 Disabled $(ThirdPartySrcDir)\$(LibNameGlew)\include;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;VC_EXTRALEAN;GLEW_STATIC;%(PreprocessorDefinitions) true true Default MultiThreadedDebug Level3 true ProgramDatabase _DEBUG;GLEW_MX;GLEW_STATIC;%(PreprocessorDefinitions) 0x0409 true MachineX64 true .\glew/lib/glew_static.bsc %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) %(PreprocessorDefinitions) openmsx-0.10.0/build/3rdparty/libvorbis.vcxproj.filters0000644000175000017500000002253412262345041024000 0ustar manuelmanuel00000000000000 {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files openmsx-0.10.0/build/3rdparty/freetype.vcxproj0000644000175000017500000026365212262345041022171 0ustar manuelmanuel00000000000000 Debug Win32 Debug x64 Developer Win32 Developer x64 Release Win32 Release x64 {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} freetype StaticLibrary false MultiByte StaticLibrary false MultiByte StaticLibrary false MultiByte true StaticLibrary false MultiByte StaticLibrary false MultiByte StaticLibrary false MultiByte true <_ProjectFileVersion>10.0.30319.1 $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameFreeType)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameFreeType)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameFreeType)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameFreeType)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameFreeType)\ $(ThirdPartyOutDir)\ $(ThirdPartyIntDir)\$(LibNameFreeType)\ Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) NDEBUG;WIN32;_LIB;FT2_BUILD_LIBRARY;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true Sync Default MultiThreaded true true true Level4 Default 4001;%(DisableSpecificWarnings) NDEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX86 X64 Full AnySuitable true Size true true true $(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) NDEBUG;WIN32;_LIB;FT2_BUILD_LIBRARY;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true Sync Default MultiThreaded true true true Level4 Default 4001;%(DisableSpecificWarnings) NDEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX64 set LibFullName=Foo Disabled $(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) _DEBUG;WIN32;_LIB;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) false false true EnableFastChecks MultiThreadedDebug true Level4 ProgramDatabase Default 4001;%(DisableSpecificWarnings) _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX86 set LibFullName=Foo X64 Disabled $(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) _DEBUG;WIN32;_LIB;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) false false true EnableFastChecks MultiThreadedDebug true Level4 ProgramDatabase Default 4001;%(DisableSpecificWarnings) _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX64 set LibFullName=Foo Disabled $(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) _DEBUG;WIN32;_LIB;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) false false true true Default MultiThreadedDebug true Level4 ProgramDatabase Default 4001;%(DisableSpecificWarnings) _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX86 set LibFullName=Foo X64 Disabled $(ThirdPartySrcDir)\$(LibNameFreeType)\include;%(AdditionalIncludeDirectories) _DEBUG;WIN32;_LIB;FT_DEBUG_LEVEL_ERROR;FT_DEBUG_LEVEL_TRACE;FT2_BUILD_LIBRARY;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) false false true true Default MultiThreadedDebug true Level4 ProgramDatabase Default 4001;%(DisableSpecificWarnings) _DEBUG;%(PreprocessorDefinitions) 0x0409 true MachineX64 Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks false Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks false Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks false Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks false MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) false MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) false Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks Disabled %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) EnableFastChecks MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) MaxSpeed %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) openmsx-0.10.0/build/flavour-bindist.mk0000644000175000017500000000030412262345041020601 0ustar manuelmanuel00000000000000# Generic flavour for binary distributions. # It disables debugging and aims for a small binary. # Optimisation flags. CXXFLAGS+=-Os -DNDEBUG -ffast-math # Strip executable? OPENMSX_STRIP:=true openmsx-0.10.0/build/sizestats.py0000644000175000017500000000636112262345041017554 0ustar manuelmanuel00000000000000from executils import captureStdout from collections import defaultdict from os.path import normpath import re, sys _reSymbolInfo = re.compile( r'^([0-9a-f]+\s)?([0-9a-f]+\s)?\s*([A-Za-z])\s([^\t]+)(\t[^\t]+)?$' ) def parseSymbolSize(objectFile): text = captureStdout(sys.stderr, 'nm -CSl "%s"' % objectFile) if text is not None: for line in text.split('\n'): if line: match = _reSymbolInfo.match(line) assert match is not None, line addr_, sizeStr, typ, name, originStr = match.groups() if originStr is None: origin = (None, None) else: source, lineNo = originStr.lstrip().rsplit(':', 1) origin = (normpath(source), int(lineNo)) if sizeStr is not None: if typ not in 'Bb': # Symbols in BSS (uninitialized data section) do not # contribute to executable size, so ignore them. yield name, typ, int(sizeStr, 16), origin if __name__ == '__main__': if len(sys.argv) == 2: executable = sys.argv[1] # Get symbol information. symbolsBySource = defaultdict(list) for name, typ, size, (source, lineNo) in parseSymbolSize(executable): symbolsBySource[source].append((name, typ, size, lineNo)) # Build directory tree. def newDict(): return defaultdict(newDict) dirTree = newDict() for source, symbols in symbolsBySource.iteritems(): if source is None: parts = [ '(no source)' ] else: assert source[0] == '/' parts = source[1 : ].split('/') parts[0] = '/' + parts[0] node = dirTree for part in parts[ : -1]: node = node[part + '/'] node[parts[-1]] = symbols # Combine branches without forks. def compactTree(node): names = set(node.iterkeys()) while names: name = names.pop() content = node[name] if isinstance(content, dict) and len(content) == 1: subName, subContent = content.iteritems().next() if isinstance(subContent, dict): # A directory containing a single directory. del node[name] node[name + subName] = subContent names.add(name + subName) for content in node.itervalues(): if isinstance(content, dict): compactTree(content) compactTree(dirTree) # Compute size of all nodes in the tree. def buildSizeTree(node): if isinstance(node, dict): newNode = {} for name, content in node.iteritems(): newNode[name] = buildSizeTree(content) nodeSize = sum(size for size, subNode in newNode.itervalues()) return nodeSize, newNode else: nodeSize = sum(size for name, typ, size, lineNo in node) return nodeSize, node totalSize, sizeTree = buildSizeTree(dirTree) # Output. def printTree(size, node, indent): if isinstance(node, dict): for name, (contentSize, content) in sorted( node.iteritems(), key = lambda (name, (contentSize, content)): ( -contentSize, name ) ): print '%s%8d %s' % (indent, contentSize, name) printTree(contentSize, content, indent + ' ') else: for name, typ, size, lineNo in sorted( node, key = lambda (name, typ, size, lineNo): ( -size, name ) ): print '%s%8d %s %s %s' % ( indent, size, typ, name, '' if lineNo is None else '(line %d)' % lineNo ) printTree(totalSize, sizeTree, '') else: print >> sys.stderr, 'Usage: python sizestats.py executable' sys.exit(2) openmsx-0.10.0/build/package-darwin/0000755000175000017500000000000012262345041020020 5ustar manuelmanuel00000000000000openmsx-0.10.0/build/package-darwin/README.html0000644000175000017500000000603312262345041021645 0ustar manuelmanuel00000000000000 openMSX README

openMSX README

Documentation

In the Documentation directory you can find the documentation for openMSX. Some sections you might want to read first:

Key Mapping
Lists which keys you need to press for special MSX key and for emulator functions.
System ROMs
Describes how openMSX deals with system ROMs. This binary distribution has C-BIOS inside of it. Should you decide to install additional system ROMs, I suggest you put them in ~/.openMSX/share/systemroms. That way, if you upgrade openMSX later, they will automatically be found by the new version.
Release History
Lists the changes made in each release of openMSX. Read this to learn about new features or changed behaviour.
Authors and License
Tells you about the people who made openMSX and the conditions for using and distributing it.

Running MSX Software

ROM files (ROM images)
You can run a ROM file by double clicking it in the Finder.
DSK files (disk images)
You can also run a DSK file by double clicking it in the Finder. Make sure you set the default machine to one that supports disks; C-BIOS does not yet have disk support. You can change the default machine by entering the console (Cmd+L) and executing for example set machine Philips_NMS_8250. The next time you start openMSX, the new default machine will be used. If you want to change disks without leaving openMSX, enter the console and type diska path/to/other.dsk.
CAS files (cassette images)
Also CAS files can be run by double clicking them in the Finder. As for disks, you will need a default machine that supports cassettes; C-BIOS does not yet support cassettes. If you want openMSX to guess the loading instructions to start the program on the cassette automatically, type set autoruncassettes on in the console.

Double clicking a file in the Finder to run it in openMSX will only work if openMSX is not already running, so make sure you close it first.

To shorten the loading times of disks and cassettes, you can type set fullspeedwhenloading on on the console.

openMSX on the Web

Home Page
Contains everything about openMSX.
Project Page on SourceForge
You can download new versions here, or report bugs.
Forum
Discuss openMSX with other users.
openmsx-0.10.0/build/package-darwin/node.mk0000644000175000017500000000016512262345041021300 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ Info.plist openmsx-logo.icns README.html \ app.mk include build/node-end.mk openmsx-0.10.0/build/package-darwin/Info.plist0000644000175000017500000000367712262345041022005 0ustar manuelmanuel00000000000000 CFAppleHelpAnchor index CFBundleDocumentTypes CFBundleTypeExtensions rom CFBundleTypeName ROM image CFBundleTypeRole Shell LSTypeIsPackage CFBundleTypeExtensions dsk CFBundleTypeName Disk image CFBundleTypeRole Shell LSTypeIsPackage CFBundleTypeExtensions cas CFBundleTypeName Cassette image CFBundleTypeRole Shell LSTypeIsPackage CFBundleExecutable openmsx CFBundleGetInfoHTML openMSX %VERSION% CFBundleIconFile %ICON% CFBundleIdentifier org.openmsx.openmsx CFBundleName openMSX CFBundlePackageType APPL CFBundleShortVersionString %VERSION% CFBundleSignature oMSX CFBundleVersion %VERSION% LSMinimumSystemVersionByArchitecture x86_64 10.6.0 i386 10.4.0 ppc 10.4.0 LSRequiresCarbon NSPrefPaneIconFile %ICON% NSPrefPaneIconLabel openMSX openmsx-0.10.0/build/package-darwin/openmsx-logo.icns0000644000175000017500000010700312262345041023326 0ustar manuelmanuel00000000000000icnsŽics#H€Ààààðø?øøø?ø?ü?üüü8€Ààààðø?øøø?ø?ü?üüü8is32ƒ‰UùÐú/ˆZÇßÖaˆ/åÜЈ*j†Xˆ2ÀÂÜÞF†8úþª…âþþý“Í2…T€þ¨‡d¥õþþ® ‚ Íдíýý§Šnž¼=Þåܹî‚ý¢‚ºäãÆØï€ýþ¶ƒ ½ÙÍýçýþþÿÓ™ƒ qŒ–̲ÁÍ¿­”‰u…‘Wƒ‰UùÐú/ˆZÆÚÖaˆ.ÞÍˈ'_fR ˆ2¦VØÞF†8úþª…âþþý“Í2…T€þ¨‡]Ÿõþþ® ‚ ¹¹¢íýý§Šnž¼4ÄǼ¬î‚ý¢‚ Â¾§Øï€ýþ¶ƒ Ÿ²°ýçýþþÿÆhƒ m‰–̲À¿¦ye‰KZa:ƒ…moUˆªýçý…L‡§¼˜Ö¡S‡eƒ‡WL‡1BE†7€|.½ê‚BƒUrûþÆR<‚ NVéþþý¼ÚzM=‚}€þÉgXPF;‚ pôþþÊ^SH>=‚ çýýÄ®–µÇ‚Uî‚ý®‚Øï€ýþ½‚ -ýçýþþÿ¥„V¨Ó¸¼gs8mkc®”{ÿÿÿâìÿÿÿÿ_óÿÿÿÿr÷ÿÿÿÿ„IÿÿÿÿÿìE ¿üÿÿÿÿÿÿýHXÿÿÿÿÿÿÿÿÿÛßÿÿÿÿÿÿÿÿÿ0&Ùÿÿÿÿÿÿÿÿÿóÿÿÿÿÿÿÿÿÿÿ2þÿÿÿÿÿÿÿÿÿÿ¡œÿÿÿÿÿÿÿÿÿþD£ÿÿÿÿÿÿÿÿÿº8CXˆ¼ÿÿÿÇJ“—#ICN#€àð?ø?ø?ø?ø?ø?ø?ø?üÿÿÿÿÿ€ÿÿÀÿÿÀÿÿàÿÿàÿÿÀÿÿàÿÿàÿÿàÿÿðÿÿðÿÿàÿÿÀÿÿàÿÿðÿÿð?ðà€€àð?ø?ø?ø?ø?ø?ø?ø?üÿÿÿÿÿ€ÿÿÀÿÿÀÿÿàÿÿàÿÿÀÿÿàÿÿàÿÿàÿÿðÿÿðÿÿàÿÿÀÿÿàÿÿðÿÿð?ðà€il32F©% –,åÿ¿‡ýî'•™ƒÿ¨•ÑÕËöõ¶ÿըѿ¦ÿÜ•IþÕØÝåÿX” }ÏôÑ꿟,”‚ åÕÚ¨_C•&.al• ÊÀ‹ôï“’!ÍþÞ»Àö€þö!&í‡þh àˆþV•ˆþØŽûƒþøS†·þþýþ€€Ú„þ7“$…þ=‘ƒ‹¶Ýƒþ‘…‡¥Ì½µ¯·‚þ ýw?=&rê†×èç翨½‹ý†Zèççåå¿’ç‰ýþy†ÍåäʧëÕ‡ýÿ,‡Žååä€ã©×¾þ…ýþÿCˆ´ä€ãâ¬ýÍÊû„ýÿÿ¬ˆ *°ããâàÄýýñ´‚ýþ€ÿÃ!ˆ¥ÀàÂÍ€ýûý€þÿñžÍŒ‰ |rŒ}ªÇöô°ùÿ¿“µ †“ &iŸ‰¸¡ÍÄ.•Oˆ±Ç‡aš7ƒ©% –,åÿ¿‡ýî'•™ƒÿ¨•ÑÕËöõ¶ÿզƸ¦ÿÜ•IþÏÍÍåÿX” yÃíÄÙ¶—)”y•ׯÊX>•#+-f• ˆC~óï“’!Íþݯ½ö€þö!&í‡þh àˆþV•ˆþØŽûƒþøS†·þþýþ€€Ú„þ7“$…þ=‘w€¯Üƒþ‘…‡–¹ª¡²‚þ ýw?=&rê†ÂÏÍ˨“»‹ý†PÍÌÊÉÆ¥€ç‰ýþy† ´ÉÇÅĪ¢ëÕ‡ýÿ,‡ {ÆÄÃÀ¾½˜×¾þ…ýþÿCˆ šÂ¿½¼¹ýÍÊû„ýÿÿ¬ˆ “¼»¸·©ýýñ´‚ýþ€ÿ¶`ˆ–Ÿ¶Æ€ýûý€þÿñy‹_‰ |k‰}ªÇöô°ùÿ½q{lY“ &e‡f~m‹„•3[_y‡ZBš#ƒŠ\mmgf–f€Œrnvpb•’óÿßÀþö‚^“k̃ÿÏ[X’ mèÕËõõ¶ÿöZV’ jÚÕ}6V¦ÿêWS’ f›þ7Ýÿ˜SP’ SF ƒ-/)PM’ @ "MI‘ U[7&PIE? >Z\Z+êô¸_A>pâþ¼Rƒö€þùmHB‹)MO\ô‡þ—ZO4‰1e[BëˆþVPB‰TeJ¯ˆþßZQLH2ˆNYQýƒþ øl¨ÇdSNHD9ˆ"Dœþþýþ ¬lfUYTOJE@:*ˆè„þ ha\VQKFA<6ˆM…þ ‚d]XRMGC=8,ˆaÖƒþ ¯^YTNID?:FJ ‡‚þ ýŸwV;=X휇€§‹ýž†‚ç‰ýþІƒ‡ëÕ‡ýÿA †ƒ9×¾þ…ýþÿVˆ‚ ýÍÊû„ýÿÿ°‰*ýýñ´‚ýþ€ÿ˜ŠA¥€ýûý€þÿñ!Š |H€‡“¹Ïøô²ùÿ·"‘ &UL?€™¤l8mkVdQöÿÿÿðj ÿÿÿÿÿÿÿNÿÿÿÿÿÿÿÿþN½ÿÿÿÿÿÿÿÿÿ õÿÿÿÿÿÿÿÿÿÚíÿÿÿÿÿÿÿÿÿäàÿÿÿÿÿÿÿÿÿâåÿÿÿÿÿÿÿÿÿãúÿÿÿÿÿÿÿÿÿþ0=ÿÿÿÿÿÿÿÿÿÿÿ²ÔÿÿÿÿÿÿÿÿÿÿÿÿÍE>Éõÿÿÿÿÿÿÿÿÿÿÿÿÿÿ÷:4öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÙ ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿƒ¶ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí aÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿZÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿe‹ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ xÛÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿFÏÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿcRÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿØ^ ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿM•ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑ Ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý:Êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç5»þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌN“›oŠÔüÿÿÿÿÿÿÿÿÿÿÿÿ¾ I«ÿÿÿÿÿÿÿ^ZÎþÿÿþŠ8@!ich#Hà?ðøÿüÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÀÿÿà?ÿÿðÿÿøÿÿÿüÿÿÿüÿÿÿþÿÿÿþÿÿÿÿÿÿÿ?ÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿÿÿÿþÿÿÿ€?ÿÿÿÀÿÿÿÀÿÿÿÀÿÿÀÿ€ÿþà?ðøÿüÿþÿþÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÀÿÿà?ÿÿðÿÿøÿÿÿüÿÿÿüÿÿÿþÿÿÿþÿÿÿÿÿÿÿ?ÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿÿÿÿþÿÿÿ€?ÿÿÿÀÿÿÿÀÿÿÿÀÿÿÀÿ€ÿþih32 ï4< £ ÇÿÿÖ'†úÿÚ¢–ÿîþ€ÿ± ð†ÿø  2ÿþ×úÿÿþàþÿÿP  3ÿà Ä×ÕÉåÿÿe  õþѹÏã¸Ëûÿý5  ŸÿÑçÐêÌäÿÿµ  @ÚÆûËêêÀÝ¿¡ o ÜåùÀêê¹Å—o ˜^Éêï€ê¤YŠ  "†F~É‚G( ‡¢ !mV6 Gg £ U±ÝéÖVÃýöªPž aéϰž™Âù€þýÄ*œµ€þêÏÂâ‚þýþÍ šÙŒþú)™Åþú+˜–Žþë˜GùŽþƒ˜Òˆþñ¸±ðþþ¥ ˜Hþþý…þð4,Ðr˜·€þýƒþýfšó‡þëŸSˆþÙŸ‘ˆþÝŸµø‡þðœM¦­˜¶û†þ~‰k Œgß±ª¤±ÇŒç…þû` ‚MÓúFŒª€èçЗÂî€þýþúàäåÊ—ll°åûþþ[‹Þ€èççãž¿ƒù’ý[‹^èè€çååã›§¶í‘ýK‹BçåÞ¢€ßú‡ýûƒýÿƒåääÒ­ÀØ×ÉŒýÿ·ŒgÞåå€ä€ãטղ·ÿŠýþÿÀ ›€ä‚ãâŽû¾âýýû‡ýþÿÿþT5·ä‚ãââ’ýúɧàú‡ýÿŽ3²âãã€â઀ýû®Ä…ýþþÿ­†(1šØââààÒÍýûǃýþƒÿô’ͦ&†š¤×àÉ”ñþƒýþ‡ÿþ”µÍÍs’e¯}w®ÞýÇí…ÿ穘¬Ìm—'J†¨¨‚.‡Ãâêé༆} Į̀+  1ƒ‡‰—¹Í¨¬ÍÍr¢ 7‚±ÊŽÍÍ™–€ ¤<7fˆ‡C)µï4< £ ÇÿÿÖ'†úÿÚ¢–ÿîþ€ÿ± ð†ÿø  2ÿþ×úÿÿþàþÿÿP  3ÿà ÄÒÏÉåÿÿe  õþÑ´ÉÒ´Ëûÿý5  ŸÿÏàÉÙ¾äÿÿµ  ?׿úÂÙٽܾ¡ k˜ÌÖô¶ÙÙ®·Žg X¹Ùã¶€Ù—R  |Bu¹zC% }¢ e Cd £ Qm C½ýöªPž aéË O0p¾ù€þýÄ*œµ€þéʾâ‚þýþÍ šÙŒþú)™Åþú+˜–Žþë˜GùŽþƒ˜Òˆþñ¸±ðþþ¥ ˜Hþþý…þð4,Ðr˜·€þýƒþýfšó‡þëŸSˆþÙŸ‘ˆþÝŸµø‡þðœEƒ–Ÿµû†þ~‰k Œ^Ë¡™“ž±„å…þû` ‚MÓúFŒ ™ÒÑÐ͸†¬yî€þýþúàäåÊ—ll°åûþþ[‹ ÈÐÏÍÌËÆ‰¦~ù’ý[‹ SÏÍÌËÊÉÇņµí‘ýK‹ :ÌËÊÉÇÆÅľŠußú‡ýûƒýÿÊÉÆÆÅÄñ“ÀØ×ÉŒýÿ·ŒXÂÆÅÄÃÂÀ¿¾´“Õ²·ÿŠýþÿÀ „ÄÃÂÀ¿¾½¼»û¾âýýû‡ýþÿÿþT*›À¿¾½¼»¹¸zýúɧàú‡ýÿŒŽ *–½½¼»¹··”€ýû®Ä…ýþþÿŸZ+‚´¹¸·¶©Äýûǃýþƒÿôt‹p†‘‹®µ ƒðþƒýþ‡ÿþŒ{‹‹N’bzŒ®}w®ÞýÇí…ÿçssht‹H—'J†¨¨‚.‡Ãâêéà¹|]lŠŠrU  &ad`h}‹qt‹‹L¢ #Vy‰`‹‹hfU¤'$E[Z-µ’ijgbU¥ UoqooljjdE¢ fv—œunmwod]  \„äÿÿꊿýÿë}`[ŸrÊÿöþ€ÿÕf]Uf€ø†ÿúw\Xm“ÿþ×úÿÿþàþÿÿ—ZWUœl’ÿà À‚vÆåÿÿ¤XUU›Uj}úþÑ^pfËûÿþ‚VTOœfjËÿª‰a ãÿÿÓ[SRUœf_t©fß;‡Ç¶dSQPMœ]C/±$  IOMKœT0FMKIœ]9€@LKIF›$[\9€UWJIGE$šE[YX8gûùÉŠLBDš @Y_ ð˜ö€þýØmK: —!G^Ò€þ䋊܂þýþÚZOH>“'>DPKèŒþúh[VK.‘=d]Z@ÚþúpYUQD7khX)µŽþîaVROH0ŽTh`KmûŽþ¢VSOLJ=.[`X1âˆþôÆ¿ñþþ¸ZUPMIFA0PYFnþþý…þñug_jÕ“XVRNJGD@8,R3Í€þý„þ”mjfYKZVSOKGEA=:.Ž$>ö‡þíkjgb_[WTPLHFB>;7&zˆþâYhd`\XTQMJFC@<86ޝˆþã[ea]YVRNKGDA=96,ŽÅø‡þñka^ZWSOKHEA>:74$2¨û†þœ^[XTPMIFC?;9=†= Œ@ä…þûˆY[TJIFC@;Fn×ûlŒ‚-î€þýþúçêçÒ©‹ˆ—»çûþþtŒƒUù’ýr‹…©í‘ýb‰†1ßú‡ýûƒýÿð4 І¾Ø×ÉŒýÿ¿‹‡nÕ²·ÿŠýþÿÇŒ†1û¾âýýû‡ýþÿÿþgŽ… ýúɧàú‡ýÿ‘„'€ýû®Ä…ýþþÿz ‚˜ýûǃýþƒÿô.†f€:îþƒýþ†ÿþþu’TQz¤޼ãýÇí…ÿçV€—$Ee˜³³8‡Ãàêéàµb¢ ƒ¥„æh8mk $%@­æññìÅe ’øÿÿÿÿÿÿÿÀ& µÿÿÿÿÿÿÿÿÿÿå8ýÿÿÿÿÿÿÿÿÿÿÿÝ(ïÿÿÿÿÿÿÿÿÿÿÿÿÿ–‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓ Ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿù*ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿW×ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿVÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿTÔÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿSåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿsüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÒBÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþi ·ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿèF‹þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞ5[Äèÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿí,ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÊnýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþƒàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó2!þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿô³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿs#Êýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ³'òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„Kþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ0rüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿAyÀîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿt¢ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”íÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ—;þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”tÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŸvÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿù4õÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓ ‡ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþcÊÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ» 0êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú86àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôq)Èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúc$ÈþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄ^µÚÞ˘¸ñÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ­ M~­°°°Ôõÿÿÿÿÿÿÿÿÿÿÿþj/¦ÿÿÿÿÿÿÿÿÿÿØfÙÿÿÿÿÿÿÿâ6M“ÁØßÙžit32*ùÿÿÿÿÿÿÿ¡6ŸØúØŸ6„4NieK毄ÿ¯€zÞÿþÑAã׆ÿ×Å…ÿöPâЈÿÐÖ‡ÿï4ásŠÿï‰ÿ ßõ—ÿûHßq™ÿ¯ßÍ™ÿêÝšÿö:Ý6›ÿ|ÝT›ÿ¯Ýi‚ÿâU?¹ÿýÿñ~eÓ…ÿåÝa‚ÿq&€ÿ ïÂÑýÿÿ~0…ÿýÝE‚ÿq&ÿù‰ÅïÓ‚Úÿe…ÿäÝ&‚ÿâU?¹éeéî¡êëißÓ0Ÿ…ÿ¯Ýñ„ÿ ùYèû’Ðêêçg‰ÿkÞ¥„ÿ‡ÉøþŒï€ê¡œ‡ÿãÞ:ƒÿè_îþþ™êïqþ†ÿŠÞÉ‚ÿuÜùþÿ–‚ê–»…ÿêà*‚ÿ|í€þƒ‚êè\„ÿõ`âe€ÿåõþþ颂êëoø‚ÿý‰à *bŽÄÓˆÕûþþÂÀƒê¢ÐÿïŸwtXà CÕô‰ââÇãöýþœØƒêÕ‹qŒÇôã¯AßAþƒ`ÆïÐçêåîþþ€éƒê ëLÙÆ©„‹¶à€Ýɼ2›„êöþqóƒê ëÇëßêíy Æï(Ýä„§·ƒêîþoõ‡êë  ë`ÜÀ®ê׃êö|ï‡ê¬Þœã ÜTåè~ Ÿí‚ê˜â„êíß^?êÕKÞ^àä7?’›ÄóêâÖêë‡a9€¨ädߍ ŠãêÞs„=ãzá&égƒ-e"†¿ã}åLwÉå¸ÞV>‰âðÃiðÓ‹Bêªûmkä ¶ßLuúùúúŠà€úé&6å–îþ²mâ±èkJÜõúú†úúöøÜ/Uè’Ù…þÓ_Þ±¨›ê±F`ÍøËùúè§6Žä|Ùˆþé`Úi€þÂuÞåªUƒžè~5ZÚ´nç‹þÕ6×+²‚þñp™êâ¤P[|ÝÀXþýþŽÓe†þ Æk“åêß„IsíþþýþùQÒ̉þʆ¡n¡øþýŽþêÐ%â”þýˆþùƒþqÎ)ï•þýˆþúƒþ»Í(ô–þýþëÌ%í—þýþûËݘþýˆþûƒþëÊÆ™þýþÜÊšþýþÍÉMýšþýþƒÈè›þýþ.ɤœþýŽþ²ÈAþýþû"ÈØþýþdÈ\ÿþýŒþ}ÈßžþýÆ€m€Æ†þÉXžþª9Å‚þóWÊäþ‹ƒ €þ½ ÊL…þù”þ¤ †¢ÝXÌ¿†þó’þäˆÌ"øœþfÚmœþè Ú¼œþ‰ÚþOÛ[þÛ•þÛóþÚžþÚYžþÚ}žþFÚÀžþoÙÞžþ¿Õ59bqŠÓœþ#Ó =r¢Æ×Çœa-CÇšþ¤Ÿ Ú_ª;žå‚éè²?8Ì™þElôþj© ©éèf_XRL_¡ãè²%iù—þê&š*¿€þ® §7ééèéè Ç~=SÀçèçã^&Þ—þëH –2Ÿû‚þf§™èéé…è ½JGÉçèç„Ò˜þ ©HTmk\‰9rÌ„þÿj¦€èé‡è¼7sççå™Ó—þûýýúþúùÄw_> Fi˜Úˆþl¦Té…èççèçr?Óååà–ýûþ…ýû‚ýúûúýþl¦ ‡è„ç±+Äçåk*ù‡ýûŠýûþžýþm¥ã€èå€èçè„çåçÄ,Âååû‚Y¸ÞÂÖÙÕàýþ€ØƒÙ ÚGÊ·z€ªÐwݼ¯/„ÙðþqâƒÙ Ú¸ÚÐÙÜp¸Þ&ÝÕ{›ªƒÙàþoã‡ÙÚ• ‚‚ÚYܲ¡ÙÇ‚ÙÚï{Þ‡ÙŸÍ‘Ò ÜNÖ×u ”Ü‚ÙÑ„ÙÜÐX:ÙÄFÞWÑÕ4:‡¶àÙÒÆÙÚÜ™‹|Z6€Ó\ß·— €ÒÙÏk„8Òqá"Ø_ƒ+] †±‚ãsÕFo»åªÍP€€=Ù¦ûmkä¨ÐF‡3ÖŽíþ²mâ¥×b ‚O׉مþÓ_Þ±¨“Ù¥, ƒƒÓuÙˆþé`Úi€þÀpÏÕŸ9SË©mç‹þÕ6×+²‚þñnÙÑ—=)pÍ´SþýþŽÓe†þ Æi‰ÕÙÐ{FsíþþýþùQÒ̉þÊ„žn øþýŽþêÐ%â”þýˆþùƒþqÎ)ï•þýˆþúƒþ»Í(ô–þýþëÌ%í—þýþûËݘþýˆþûƒþëÊÆ™þýþÜÊšþýþÍÉMýšþýþƒÈè›þýþ.ɤœþýŽþ²ÈAþýþû"ÈØþýþdÈ\ÿþýŒþ}ÈßžþýÆ€m€Æ†þÉXžþª9Å‚þóWÊäþ‹ƒ €þ½ ÊL…þù”þ¤ †¢ÝXÌ¿†þó’þäˆÌ"øœþfÚmœþè Ú¼œþ‰ÚþOÛ[þÛ•þÛóþÚžþÚYžþÚ}žþFÚÀžþoÙÞžþ¿Õ23bpŠÓœþ#Ó 7h”´Ã´W*CÆšþ¤Ÿ Ú_ª5ÐÕÓÓ€ÒÑÑР88Ì™þElôþj© ›ÕÕ\VPJEV’Ì€ÐÏŸ iù—þê&š*¿€þ® §2ÕÕÓÓ€Ò Ñ´q6KªÏÏÍÊS&Ý—þëH –2Ÿû‚þf§‹ÓÓ€ÒÑрРϨC@±ÍÌÌuÒ˜þ ©HTmk\‰9rÌ„þÿj¦ҀрÐÏÏÍͦ1f€Ë‡Ó—þûýýúþúùÄw_> Fi˜Úˆþl¦L€ÒÑÑ€ÐÏÏÍ ÌËe7¹ÊÉ|à–ýûþ…ýû‚ýúûúýþl¦‘ÒÑÐ€Ï€Í ÌÌËËÊœ&¬ÉÇ]*ù‡ýûŠýûþžýþm¦ÌÑÐÐÍÐÏÍÌ€Ë ÊÊÉʬ&¨ÇÆ4g•ýûþžýþl¥-ЀÏÍÍ€ÌËËÊÊ€ÉÇ­'¯Æ² ¼”ýûþžýþg¥PÐЀÏÍÍÌÌ€Ë€Ê€É€Ç §,¾Å{ùýýÝýûþžýþ\¥tÐÏÍÌÌ€ËÊÊÉÆ ÅŽHÄÄ(~ýýÕýûþŸýD¥“€ÍÌÌ‚ËÊÉǀƀÅÄWÙ îýÄýûþˆýú‡ýû‚ýþþýýþñ¥tÍÍÌÌËÊÉÇÇ€ÆÅÅ€ÄÃ1´Â4zýø¿ýûþˆýùˆýû€ýþ€ÿýþÂ¥:ÍÌÌËˀʀÉÇÇ€Æ€Å€Ä Ã›OÀ—ñý²ýûþ˜ýÿþþr§¸€ËÊÊ€ÉÇÇ€ÆÅŀĀÀÂLÀ¿&˜ûÍÐŽýûþ˜ýþ€ÿþó§OËËÊÊ€ÉÇÆÅÅ€ÄÃÃÂÀ¬¿¿w8ýý“€ýôâôˆýûþ™ýÿ¬¨¼€É€Ç€ÆÅŀĀÀÂÀÀ¿ ¸ äýË´ýýkkýþ†ýûþ˜ýþÿj©TÉÉÇÇ€ÆÅÅ‚ÄÀÂÀÀ€¿¾¾½½?‰ýýéýFFþÿþ…ýûþ˜ý‚ÿF© °ÇÆÅÅ€ÄÃÃÂÀ€¿¾¾½ ‚9ýýíkýڔ݀ÿþ„ýûþ˜ýþÿÑ ¨4€ÆÅÅÄÿÀ¿¿¾¾½ ¼¼¶õýýËwýþÿþ„ýûþ–ýþ„ÿѪrÆÅÄ€ÃÂÂÀÀ€¿¾¾€½¼¼€»,¸€ý¢uþ€ÿõ…ýûþ•ýþ…ÿý©¦ÄÀÂÀÀ€¿¾¾€½¼¼»¹`wýœhùÿþþýú€ýûþ•ýþ†ÿ¯ª½ÃÀ€¿¾‚½¼¼»»¹¹¸C‚ýÀCÕƒýð€ýûþ”ýþ‡ÿÕ«AÃÂÂÀÀ¿¾¾½½€¼»¹€¸´!ƒýçbJŽó€ýð€ýûþ”ýˆÿêªOÂÂÀ€¿¾¾€½¼¼€»¹¹€¸··¶#ù„ý׎,Žƒýûþ“ýþˆÿþ© M½¿¿¾½€¼€»¹¹€¸··¶¢^û†ýôg=çýûþ’ýþ‰ÿí;R ª<»¾¾½¼»¹¹¸¸€·¶¶|”‰ý¡%߀ýûþýþþŠÿ½L‹„6«%ª€½¼¼€»¹¸··€¶µRèŠý±'ðýýûþŒý€þÿpf€‹Zª‰¼»¹¸··€¶µµ§¼ýûŠý„zýýûþŠýþ‘ÿ$ˆ‹g«W»»€¹¸¸··€¶€µ´DêûŒýíýýûþ‰ýþ‘ÿ“8ƒ‹K«¶N|¸·¸··€¶€µ´´lPùýûþýþþÿþÿ‚þýÿý€ÿã{„‹ «È¥Dw°‚¶µ€´aAö€þŽýûþþ‘ÿþþÿþþÿÿù0H…‹[¬ Þ•@R ¶µ€´,Pãöþÿÿþýûþþ–ÿþ€ÿùH/†‹f®iǹO$HM=-nÐûôýù€´ûþŒýûþþ™ÿå0+Š‹9„‹M¯7:z’¼ÜôýîÖ§u6 S°‹ýû‹¬˜ÿ­9€‹_#u‚‹+²*† I~»úþ…ýûêU<Í”ÿÍ= ^€‹‰‹y'5{€‹Ç Uw¿çùýþþýùÏX;©ûŽÿûª;=‚|€‰‹g&$PS× M•Úˆÿ Ú˜M Gƒ‹‹.†‹€TÛ)EZglgZE) Ao‚‹Ee†‹gÝ PIA<=CN_v†‹‚ ˆ„‹ŠÞl†‹Pdƒ‹`1„‹8â,y„‹u6„‹K.„‹Iå,kƒ‹$„‹be‹‹Fè?l€‹e3…‹6$6 ë9P"E‡‡Š‹{= ò(3+ÿÿÿÿÿ°fanpq€ommlf]îgppqsqrqpoonnlkigfégqr€sqqppo€nmllkiiffåfoqssrrqqppoonmml€kjigf[ã]qrssr€qponnmmllkkjjiihgfdá mss˜ÐíýíЖo€nmmkz‘ ¯¬yggfedPÞlrØ„ÿ×|mmls¸îÿþç•g€e_PÜmr‚ë†ÿê|k{à…ÿúed€bPÚ]rzèˆÿè‚ê‡ÿö‹bba^_PØfqr¹Šÿö‰ÿÝka``]\Øopú—ÿþ–``^^\YÖgpq·™ÿÒ`^^]]ZHÕoqqç™ÿóo^]\\ZX3Ó^oq‚šÿú‹]\\[[XUÓnpo•›ÿ²\\[ZZXSÒfmoo¦›ÿÑ[€ZYXSÒ`lon±‚ÿâU?¹ÿþÿñ~eÓ…ÿïZZYYXXWÒlnnm­‚ÿq&€ÿ ê±Çýÿÿ~0…ÿþYYXXWWUCÑlmmlœ‚ÿq&ÿùV!Ùÿe…ÿïYXXWWVUJÐNkmlk‰‚ÿâU?¹é @!ßÓ0Ÿ…ÿÐXWWVVUTUÐ?kpù„ÿù,Ó—D‰ÿ¦WVVUUTTRÑjkkjiЄÿ{þfœ‡ÿ ïmVVUUTTSTÑijiih’ƒÿè#ùûC‚=þ†ÿ¸XVU€TRRUÑgiihghâ‚ÿj±þþ,‚ »…ÿónUUTTRVÑiihgff†‚ÿ"îýþ„K„ÿøžUTTS€RQPTÑdggf€e¨€ÿå€þûêƒø‚ÿ þ²[TSSRRQQPOUÑfgfe`;>WRiɈÜøþŸ„ÐÿïœsqqRTSSRRQPPOOPÑcfeL+Êï|4,ïþq… €I`w„QRQ€PONNPÑae`)Äo‚#öýqˆ€  Q€PON€MÑad:ƒþq… IPONNMMLMÑ`b+ƒ!øq‰ EONMM€LKÑ_b9„‰qˆNNMMLLKJKÑ^aX €ƒ‡ JNMMLKK€JÑ^`_G €…#KNMLLKKJIILÑ__^^P„…JMMLLK€JIHH6Ð\^^]\[$„‡@M€LKJII€HGDÏ?\]€\[K7MLLKJJIIHHGFFI5ÎZ]\\[€ZDƒ€‚eý¡ KJJI€HGFCÎ C\\[ZZYYX,€‡€(êþÍŸHHGFEEC6ÍJ\[ZZYYXWW$‚Í…þä”KF€E€DÍ^€ZY€XWVѦ„ Ùˆþð”GDCCBF(ËZZY€XWV¥€þ»‚€XÞ‹þãsCD(Ê5YYXXWV|Ò‚þíR þýþ±PWTRNJÈ^€XWX¡†þÀ=píþþýþ ù‰YXSJ""(ÄQYX+Rlà‰þÉnomŽöþýŽþ êUY\ZSSQS5Á M_V9vî”þýˆþùƒþ uOY^\\XQR@¼ +^aZ4zõ•þýˆþúƒþ »2\]\ZYSQR$¹ %>Tagd`]+wù–þýþ ë*]\[ZXVTQF¶ Fnmjgea^" uô—þýþ û2][ZXWVTRQD.µ =qomjge`^9hê˜þýˆþûƒþ ëF\ZYXVTSRQP7³ Tspmjg+_^IPÝ™þýþ Ü:ZYXWUTRQPPM!² Rspmjge2P[ 7»šþýþÍC\XWVTSRPONL?° =rpmjgeaF1"›þýþˆW[XVTSRPONLPB3¯ qpmkgea^\\ñ›þýþL\XVUSRQONLKKE;$® 8pmjgea^[Y2ÆœþýŽþ·D\VUTRQPNMKJLP?9¬ hmkgea^\X2‡þýþûA]XUTSQPOMLKIHNF;)¬ 4mkhea^\YVHçþýþrV\VTSRPOMLKIHGGE<: « Akhea^\XV+˜ÿþýŒþ†C\XUTRPONMKJHHFFD?;)« Cheb^\YVSHëžþýÆ‚m‚Ɔþ„@\XVTRQPNMLJIHFFECA;:ª 9eb^\YVS+–žþ¹:JfjV=JÅ‚þôhJ]ZVTSRPOMLKIHGFECBA<;'ª a_\YVSQ<îþ Aomkja]`?€þ¿?TY[WTSRPONMKJHGFEDBA@>;9ª Z\YVSP6 Œ…þù”þ)¦Gomlkih`\^4¦Ý_7[ZXVUTRQPNMLJHGFEDCA@?=;;#ª2ZVTQN.׆þó’þ*äIonmkjhged_IGZ\ZXWUTSRPNMLJIHFFDCB@?=<;;0ª PTQNIiúœþ+lqnmkjigfebZ4\][ZXWVTSRPONLKIHGFECBA??=;::9ªPNL(¡œþê=omlkihf€b `^^\ZYXVTSRQPNMKJHGFEDCA@>=;;97:%«EI9Õœþ,Ž]mlkihgedb`_^\ZZXVVTRQPNLLJIGFEDCA@?=<:9878¬[þ,\jmkjhgfeba_^][ZXWVTSRPNNLJIHFFECBA?><;9876;®”þ(>bkkihfeba`^]\ZYUVTSRPONLKJHGFEDBA@>=;:97€6®-»þ+1hgihfebb`_^\ZYXUUTRPPNLKJHGFEDCA@?=<;98664.¯Eöþ*€h'gfda`_^\[ZXWVTRQPNMLJIHFFECB@?=<;987658®gžþ&.hfgfeba`^][ZXWVUSRPOMLKJHFFECBA@><;987€69 ®‘žþ*;`ffeba`^]\ZYWVUSRQONMKJHGFEDCA@>=;:976644¯&©žþ+Zhgeda`_]\ZYXVUTRQPNLLJIGFEDCA@?=<;986653+ ®7Öžþ$qgedb`_^\[ZXWVTRRPOMLJIGFFECBA?><;:87€61$$®Fêžþ+¿@`ba`^\\ZXXVTSRPONLKJHGFECBA?>=;:976799).(®`l„Òœþ"?aa`_]\ZYXVUSRPONMKJIGFEDCA??=<;989€:)/.$ «€?šþ¥K`_]\ZZXWUTRQPNMLJHHFFDCA@?=<:‚941܉.-&§‡7Ê™þ*VZ`\[ZXWVTSRPOMLKIHFFECBA?>=;988:=2pôþ-,%¥€‚dù—þ"ë7[^\YWVUSRPONLKJHGFEDBA?><;??76+=¿€þÃ8,!¥…!Ö—þ ëUJ^XXVTRPXXMKJHGFEDBA@?>=>@=+A¥ý‚þŠ+" ¥ˆ ИþªYAM~sle4&FDIHFFECA@@?<6+GsÍ„þÿ‡$ #¤‹€Ó—þûýýúþúùÆzfV& €&8Vk˜Üˆþ‚$¤à–ýûþ…ýû‚ý€ûýþ‚$¤Ž*ù‡ýûŠýûþžýþ‚!£‘g•ýûþžýþ£‘¼”ýûþžýþ|€£’ ùýýÝýûþžýþr “uýýÕýûþŸý\ž’ îýÄýûþˆýú‡ýû‚ýþþýýþô4‚ž”zýø¿ýûþˆýùˆýû€ýþ€ÿýþÉ€ŸŒ…ñý²ýûþ˜ýÿþþ‚Ÿ‹ƒ ˜ûÍÐŽýûþ˜ýþ€ÿþô4ƒ ‡8ýý“€ýôâôˆýûþ™ýÿµ„¡“‚ äýË´ýýkkýþ†ýûþ˜ýþÿzƒ¡˜ ‰ýýéýFFþÿþ…ýûþ˜ý‚ÿYƒ ¢— 9ýýíkýڔ݀ÿþ„ýûþ˜ýþÿÖ6¤—õýýËwýþÿþ„ýûþ–ýþ„ÿÖ ¥˜¸€ý¢uþ€ÿõ…ýûþ•ýþ…ÿý,§•wýœhùÿþþýú€ýûþ•ýþ†ÿ¸¨‡„C‚ýÀCÕƒýð€ýûþ”ýþ‡ÿØ©–ƒýçbJŽó€ýð€ýûþ”ýˆÿê «Š„ù„ý׎,Žƒýûþ“ýþˆÿþ¯‡†=û†ýôg=çýûþ’ýþ‰ÿí®€‹‚‰ý¡%߀ýûþýþþŠÿ½­‘ ׊ý±'ðýýûþŒý€þÿo­…ƒ¯ýûŠý„zýýûþŠýþ‘ÿƒ¬‚†åûŒýíýýûþ‰ýþ‘ÿ“ƒ¬¶F…>ðýûþýþþÿþÿ‚þýÿúþÿÿㅬȥ&‡6î€þŽýûþþ‘ÿþþÿûûÿÿù0†­ Þ•,‚3Óøþÿÿþýûþþÿþƒÿþ€ÿùH‡®iǹM€ \¯ôÝöùÂýþŒýûþþ™ÿå0€…¯7:z’¼ÜóûíÕu- 5o¾‹ýû‹¬˜ÿ­‚ƒ³*„+e“Åúþ…ýûêU<Í”ÿÍ=Ä=m‹ÉêùýþþýùÕe;©ûŽÿûª;€‚É€€ M•Úˆÿ Ú˜M ˆÝ(EZglgZE) Žà†â„å……‚胆ꂆò†ÿÿÿÿt8mk@ J†Ž’””Šzz¾ôÿÿÿÿÿÿÿÿÿûç“J ‡ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü¿M Yëÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüµ´ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîVHÇÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø†Rïÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°Hîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¶Äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ¶ ²ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ‚WüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðSéÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿä…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý¬ êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÃzÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï@ ¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþLøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”zÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç–ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú äÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxòÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽÜÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ’Àÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”ªÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ“Œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŠÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿˆšÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‰¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšÅÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿïäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ{þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉ-ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ„rÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿç¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‚!ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿËœÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¾aúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿº>!ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿåfØÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿòxPÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø‚‡ƒÌÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøŒ‘êÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúcfÞÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðM ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿé…ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ’kîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêS+èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿгÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¢BýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüBŽÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÐÖÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúSÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÌ  ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿõJÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼¼ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞ8œÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ˜LÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÑ Þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿü:Yÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¤}ûÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÚaèþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà. ‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿšzþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿrÂÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ½#þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþF_èÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþyzÝÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‹Œäÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ¼ L…ÌøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿîuÁøþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿú)¯ýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿxÕÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ† òÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒbþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘¿ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ”\öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ‘˜ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŒÐÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ† üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿx"xY?úÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüŒðÿÿ’tÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿûkÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý1ùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ±¤ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿz4þÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÞ-ºþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ 9ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿó,³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ #ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿè0jîÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿøz¦öÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ·Ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×!&ìÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿå9Aøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿø“ Qòþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿû’ =ÖüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÜ)(Ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿï5Âýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿêqõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþ´¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ+ žÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþr‰Õõÿÿÿÿÿÿÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿu €ðÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿP.zÑõþÿÿÿüÿýúÝ—h¤ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿJp~|tS% Y«ðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýÿÿÿÿÿÿÿÿê8tªÙüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþýÿÿÿÿÿÿÿÿÿ˜'D[mx€€€€€€€€€¤óÿüÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿT RšÔþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÈ.ÊþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿA!ÓþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþŠ ŽõöÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÆ2·úþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÓ>°üÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿý—r½ôÿÿÿÿÿÿÿÿÿÿÿÿÿÿïQ @sŸÂæîíüÿÿÿê³iopenmsx-0.10.0/build/package-darwin/app.mk0000644000175000017500000000331112262345041021127 0ustar manuelmanuel00000000000000# Create an application directory for Darwin. APP_SUPPORT_DIR:=build/package-darwin APP_DIR:=$(BINDIST_DIR)/openMSX.app APP_EXE_DIR:=$(APP_DIR)/Contents/MacOS APP_PLIST:=$(APP_DIR)/Contents/Info.plist APP_RES:=$(APP_DIR)/Contents/Resources APP_ICON:=$(APP_RES)/openmsx-logo.icns # Override install locations. INSTALL_BINARY_DIR:=$(APP_EXE_DIR) INSTALL_SHARE_DIR:=$(APP_DIR)/share INSTALL_DOC_DIR:=$(BINDIST_DIR)/Documentation PACKAGE_FULL:=$(shell PYTHONPATH=build $(PYTHON) -c \ "import version; print version.getVersionedPackageName()" \ ) BINDIST_PACKAGE:=$(BUILD_PATH)/$(PACKAGE_FULL)-mac-$(OPENMSX_TARGET_CPU)-bin.dmg BINDIST_README:=$(BINDIST_DIR)/README.html BINDIST_LICENSE:=$(INSTALL_DOC_DIR)/GPL.txt # TODO: What is needed for an app folder? app: install $(APP_PLIST) $(APP_ICON) bindist: app $(BINDIST_README) $(BINDIST_LICENSE) @echo "Creating disk image:" @hdiutil create -srcfolder $(BINDIST_DIR) \ -volname openMSX \ -imagekey zlib-level=9 \ -ov $(BINDIST_PACKAGE) @hdiutil internet-enable -yes $(BINDIST_PACKAGE) $(APP_PLIST): $(APP_DIR)/Contents/%: $(APP_SUPPORT_DIR)/% bindistclean @echo " Writing meta-info..." @mkdir -p $(@D) @sed -e 's/%ICON%/$(notdir $(APP_ICON))/' \ -e 's/%VERSION%/$(PACKAGE_DETAILED_VERSION)/' < $< > $@ @echo "APPLoMSX" > $(@D)/PkgInfo $(APP_ICON): $(APP_RES)/%: $(APP_SUPPORT_DIR)/% bindistclean @echo " Copying resources..." @mkdir -p $(@D) @cp $< $@ $(BINDIST_README): $(APP_SUPPORT_DIR)/README.html @echo " Copying README..." @mkdir -p $(@D) @cp $< $@ $(BINDIST_LICENSE): doc/GPL.txt app @echo " Copying license..." @mkdir -p $(@D) # Remove form feeds from the GPL document, so Safari will treat it as text. @awk '!/\f/ ; /\f/ { print "" }' $< > $@ openmsx-0.10.0/build/systemfuncs2code.py0000644000175000017500000000145212262345041021017 0ustar manuelmanuel00000000000000from systemfuncs import systemFunctions from outpututils import rewriteIfChanged import sys def iterSystemFuncsHeader(functionResults): yield '// Automatically generated by build system.' for makeName in sorted( func.getMakeName() for func in systemFunctions ): yield '#define HAVE_%s %d' % (makeName, functionResults[makeName]) def getSystemFuncsInfo(): return dict.fromkeys( (func.getMakeName() for func in systemFunctions), False ) if __name__ == '__main__': if len(sys.argv) == 2: rewriteIfChanged( sys.argv[1], iterSystemFuncsHeader(getSystemFuncsInfo()) ) else: print >> sys.stderr, \ 'Usage: python systemfuncs2code.py CONFIG_HEADER ' print >> sys.stderr, \ 'Note: Should only be called directly on systems where the probe ' \ 'does not work.' sys.exit(2) openmsx-0.10.0/build/platform-solaris.mk0000644000175000017500000000023312262345041020770 0ustar manuelmanuel00000000000000# Configuration for Solaris machines. # Does platform support symlinks? USE_SYMLINK:=true # File name extension of executables. EXEEXT:= LIBRARYEXT:=.so openmsx-0.10.0/build/install.py0000644000175000017500000001015012262345041017160 0ustar manuelmanuel00000000000000from fileutils import ( installDirs, installFile, installSymlink, installTree, scanTree ) from makeutils import extractMakeVariables, parseBool from os import listdir from os.path import basename, expanduser, isdir, splitext import os import sys def installAll( installPrefix, binaryDestDir, shareDestDir, docDestDir, binaryBuildPath, targetPlatform, cbios, symlinkForBinary ): platformVars = extractMakeVariables( 'build/platform-%s.mk' % targetPlatform, dict.fromkeys( ('COMPILE_FLAGS', 'LINK_FLAGS', 'TARGET_FLAGS', 'COMPILE_ENV', 'LINK_ENV'), '' ) ) binaryFileName = 'openmsx' + platformVars.get('EXEEXT', '') docNodeVars = extractMakeVariables('doc/node.mk') docsToInstall = [ 'doc/' + fileName for fileName in docNodeVars['INSTALL_DOCS'].split() ] print ' Executable...' installDirs(installPrefix + binaryDestDir) installFile( binaryBuildPath, installPrefix + binaryDestDir + '/' + binaryFileName ) print ' Data files...' installDirs(installPrefix + shareDestDir) installTree('share', installPrefix + shareDestDir, scanTree('share')) print ' Documentation...' installDirs(installPrefix + docDestDir) for path in docsToInstall: installFile(path, installPrefix + docDestDir + '/' + basename(path)) installDirs(installPrefix + docDestDir + '/manual') for fileName in listdir('doc/manual'): if splitext(fileName)[1] in ('.html', '.css', '.png'): installFile( 'doc/manual/' + fileName, installPrefix + docDestDir + '/manual/' + fileName ) if cbios: print ' C-BIOS...' installFile( 'Contrib/README.cbios', installPrefix + docDestDir + '/cbios.txt' ) installTree( 'Contrib/cbios', installPrefix + shareDestDir + '/machines', scanTree('Contrib/cbios') ) if hasattr(os, 'symlink'): print ' Creating symlinks...' for machine, alias in ( ('Toshiba_HX-10', 'msx1'), ('Philips_NMS_8250', 'msx2'), ('Panasonic_FS-A1WSX', 'msx2plus'), ('Panasonic_FS-A1GT', 'turbor'), ): installSymlink( machine + ".xml", installPrefix + shareDestDir + '/machines/' + alias + ".xml" ) if symlinkForBinary and installPrefix == '': def createSymlinkToBinary(linkDir): if linkDir != binaryDestDir and isdir(linkDir): try: installSymlink( binaryDestDir + '/' + binaryFileName, linkDir + '/' + binaryFileName ) except OSError: return False else: return True else: return False success = createSymlinkToBinary('/usr/local/bin') if not success: createSymlinkToBinary(expanduser('~/bin')) def main( installPrefix, binaryDestDir, shareDestDir, docDestDir, binaryBuildPath, targetPlatform, verboseStr, cbiosStr ): customVars = extractMakeVariables('build/custom.mk') try: verbose = parseBool(verboseStr) cbios = parseBool(cbiosStr) symlinkForBinary = parseBool(customVars['SYMLINK_FOR_BINARY']) except ValueError, ex: print >> sys.stderr, 'Invalid argument:', ex sys.exit(2) if installPrefix and not installPrefix.endswith('/'): # Just in case the destination directories are not absolute. installPrefix += '/' if verbose: print 'Installing openMSX:' try: installAll( installPrefix, binaryDestDir, shareDestDir, docDestDir, binaryBuildPath, targetPlatform, cbios, symlinkForBinary ) except IOError, ex: print >> sys.stderr, 'Installation failed:', ex sys.exit(1) if verbose: print 'Installation complete... have fun!' print ( 'Notice: if you want to emulate real MSX systems and not only the ' 'free C-BIOS machines, put the system ROMs in one of the following ' 'directories: %s/systemroms or ' '~/.openMSX/share/systemroms\n' 'If you want openMSX to find MSX software referred to from replays ' 'or savestates you get from your friends, copy that MSX software to ' '%s/software or ' '~/.openMSX/share/software' ) % (shareDestDir, shareDestDir) if __name__ == '__main__': if len(sys.argv) == 9: main(*sys.argv[1 : ]) else: print >> sys.stderr, \ 'Usage: python install.py ' \ 'DESTDIR INSTALL_BINARY_DIR INSTALL_SHARE_DIR INSTALL_DOC_DIR ' \ 'BINARY_FULL OPENMSX_TARGET_OS INSTALL_VERBOSE INSTALL_CONTRIB' sys.exit(2) openmsx-0.10.0/build/node-end.mk0000644000175000017500000000172312262345041017170 0ustar manuelmanuel00000000000000# Should be included at the end of each node.mk file. # Process this node. SOURCES_FULL+=$(sort \ $(addprefix $(CURDIR),$(addsuffix .cc, \ $(SRC_HDR) $(SRC_HDR_true) $(SRC_ONLY) $(SRC_ONLY_true) \ ))) DIST_FULL+=$(sort \ $(addprefix $(CURDIR),$(addsuffix .cc, \ $(SRC_HDR_) $(SRC_HDR_false) $(SRC_ONLY_) $(SRC_ONLY_false) \ ))) HEADERS_FULL+=$(sort \ $(addprefix $(CURDIR),$(addsuffix .hh, \ $(SRC_HDR) $(SRC_HDR_true) $(HDR_ONLY) $(HDR_ONLY_true) \ ))) DIST_FULL+=$(sort \ $(addprefix $(CURDIR),$(addsuffix .hh, \ $(SRC_HDR_) $(SRC_HDR_false) $(HDR_ONLY_) $(HDR_ONLY_false) \ ))) DIST_FULL+=$(sort \ $(addprefix $(CURDIR),$(DIST) node.mk) \ ) # Process subnodes. ifneq ($(SUBDIRS),) SUBDIRS:=$(addsuffix /,$(SUBDIRS)) SUBDIRSTACK:=$(SUBDIRS) $(SUBDIRSTACK) include $(addprefix $(CURDIR),$(addsuffix node.mk,$(SUBDIRS))) endif # Pop current directory off directory stack. CURDIR:=$(firstword $(DIRSTACK)) DIRSTACK:=$(wordlist 2,$(words $(DIRSTACK)),$(DIRSTACK)) openmsx-0.10.0/build/platform-freebsd.mk0000644000175000017500000000027612262345041020735 0ustar manuelmanuel00000000000000# Configuration for FreeBSD. # Does platform support symlinks? USE_SYMLINK:=true # File name extension of executables. EXEEXT:= LIBRARYEXT:=.so COMPILE_FLAGS+=-D_REENTRANT -D_THREAD_SAFE openmsx-0.10.0/build/compilers.py0000644000175000017500000000766412262345041017527 0ustar manuelmanuel00000000000000from msysutils import msysActive, msysPathToNative from os import environ from shlex import split as shsplit from subprocess import PIPE, STDOUT, Popen if msysActive(): def fixArgs(args): for arg in args: if arg.startswith('-I') or arg.startswith('-L'): yield arg[ : 2] + msysPathToNative(arg[2 : ]) elif arg.startswith('/'): yield msysPathToNative(arg) else: yield arg else: def fixArgs(args): return iter(args) class _Command(object): @classmethod def fromLine(cls, commandStr, flagsStr): commandParts = shsplit(commandStr) flags = shsplit(flagsStr) env = {} while commandParts: if '=' in commandParts[0]: name, value = commandParts[0].split('=', 1) del commandParts[0] env[name] = value else: return cls( env, commandParts[0], list(fixArgs(commandParts[1 : ] + flags)) ) else: raise ValueError('No command specified in "%s"' % commandStr) def __init__(self, env, executable, flags): self.__env = env self.__executable = executable self.__flags = flags mergedEnv = dict(environ) mergedEnv.update(env) self.__mergedEnv = mergedEnv def __str__(self): return ' '.join( [ self.__executable ] + self.__flags + ( [ '(%s)' % ' '.join( '%s=%s' % item for item in sorted(self.__env.iteritems()) ) ] if self.__env else [] ) ) def _run(self, log, name, args, inputSeq, captureOutput): commandLine = [ self.__executable ] + args + self.__flags try: proc = Popen( commandLine, bufsize = -1, env = self.__mergedEnv, stdin = None if inputSeq is None else PIPE, stdout = PIPE, stderr = PIPE if captureOutput else STDOUT, ) except OSError, ex: print >> log, 'failed to execute %s: %s' % (name, ex) return None if captureOutput else False inputText = None if inputSeq is None else '\n'.join(inputSeq) + '\n' stdoutdata, stderrdata = proc.communicate(inputText) if captureOutput: assert stderrdata is not None messages = stderrdata else: assert stderrdata is None messages = stdoutdata if messages: log.write('%s command: %s\n' % (name, ' '.join(commandLine))) if inputText is not None: log.write('input:\n') log.write(inputText) if not inputText.endswith('\n'): log.write('\n') log.write('end input.\n') # pylint 0.18.0 somehow thinks 'messages' is a list, not a string. # pylint: disable-msg=E1103 messages = messages.replace('\r', '') log.write(messages) if not messages.endswith('\n'): log.write('\n') if proc.returncode == 0: return stdoutdata if captureOutput else True else: print >> log, 'return code from %s: %d' % (name, proc.returncode) return None if captureOutput else False class CompileCommand(_Command): __expandSignature = 'EXPAND_MACRO_' def compile(self, log, sourcePath, objectPath): return self._run( log, 'compiler', [ '-c', sourcePath, '-o', objectPath ], None, False ) def expand(self, log, headers, *keys): signature = self.__expandSignature def iterLines(): for header in headers: yield '#include %s' % header for key in keys: yield '%s%s %s' % (signature, key, key) output = self._run( log, 'preprocessor', [ '-E', '-' ], iterLines(), True ) if output is None: if len(keys) == 1: return None else: return (None, ) * len(keys) else: expanded = {} for line in output.split('\n'): if line.startswith(signature): key, value = line[len(signature) : ].split(' ', 1) value = value.strip() if key in keys: if value != key: expanded[key] = value else: log.write( 'Ignoring macro expand signature match on ' 'non-requested macro "%s"\n' % key ) if len(keys) == 1: return expanded.get(keys[0]) else: return tuple(expanded.get(key) for key in keys) class LinkCommand(_Command): def link(self, log, objectPaths, binaryPath): return self._run( log, 'linker', objectPaths + [ '-o', binaryPath ], None, False ) openmsx-0.10.0/build/msysutils.py0000644000175000017500000000430312262345041017571 0ustar manuelmanuel00000000000000from os import environ from os.path import isfile from subprocess import PIPE, Popen import sys def _determineMounts(): # The MSYS shell provides a Unix-like file system by translating paths on # the command line to Windows paths. Usually this is transparent, but not # for us since we call GCC without going through the shell. # Figure out the root directory of MSYS. proc = Popen( [ msysShell(), '-c', '"%s" -c \'import sys ; print sys.argv[1]\' /' % sys.executable.replace('\\', '\\\\') ], stdin = None, stdout = PIPE, stderr = PIPE, ) stdoutdata, stderrdata = proc.communicate() if stderrdata or proc.returncode: if stderrdata: print >> sys.stderr, 'Error determining MSYS root:', stderrdata if proc.returncode: print >> sys.stderr, 'Exit code %d' % proc.returncode raise IOError('Error determining MSYS root') msysRoot = stdoutdata.strip() # Figure out all mount points of MSYS. mounts = { '/': msysRoot + '/' } fstab = msysRoot + '/etc/fstab' if isfile(fstab): try: inp = open(fstab, 'r') try: for line in inp: line = line.strip() if line and not line.startswith('#'): nativePath, mountPoint = ( path.rstrip('/') + '/' for path in line.split() ) mounts[mountPoint] = nativePath finally: inp.close() except IOError, ex: print >> sys.stderr, 'Failed to read MSYS fstab:', ex except ValueError, ex: print >> sys.stderr, 'Failed to parse MSYS fstab:', ex return mounts def msysPathToNative(path): if path.startswith('/'): if len(path) == 2 or (len(path) > 2 and path[2] == '/'): # Support drive letters as top-level dirs. return '%s:/%s' % (path[1], path[3 : ]) longestMatch = '' for mountPoint in msysMounts.iterkeys(): if path.startswith(mountPoint): if len(mountPoint) > len(longestMatch): longestMatch = mountPoint return msysMounts[longestMatch] + path[len(longestMatch) : ] else: return path def msysActive(): return environ.get('OSTYPE') == 'msys' or 'MSYSCON' in environ def msysShell(): return environ.get('MSYSCON') or environ.get('SHELL') or 'sh.exe' if msysActive(): msysMounts = _determineMounts() else: msysMounts = None if __name__ == '__main__': print 'MSYS mounts:', msysMounts openmsx-0.10.0/build/flavour-android-debug.mk0000644000175000017500000000055612262345041021662 0ustar manuelmanuel00000000000000# Android flavour. Copy all ANDROID_CXXFLAGS into TARGET_FLAGS # Otherwise, probe fails as it won't be able to find the include files include build/flavour-android.mk TARGET_FLAGS+=-fPIE #CXXFLAGS+=-O0 -g -DDEBUG -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC \ # -D_GLIBCPP_CONCEPT_CHECKS CXXFLAGS+=-DDEBUG -O0 -fPIE # Strip executable? OPENMSX_STRIP:=false openmsx-0.10.0/build/thirdparty_download.py0000644000175000017500000000546312262345041021606 0ustar manuelmanuel00000000000000from checksum import verifyFile from components import requiredLibrariesFor from configurations import getConfiguration from download import downloadURL from extract import TopLevelDirRenamer, extract from libraries import allDependencies, librariesByName from packages import getPackage from patch import Diff, patch from os import makedirs from os.path import isdir, isfile, join as joinpath from shutil import rmtree import sys # TODO: Make DirectX headers for MinGW a package and make the DirectX sound # driver a component. def downloadPackage(package, tarballsDir): if not isdir(tarballsDir): makedirs(tarballsDir) filePath = joinpath(tarballsDir, package.getTarballName()) if isfile(filePath): print '%s version %s - already downloaded' % ( package.niceName, package.version ) else: downloadURL(package.getURL(), tarballsDir) def verifyPackage(package, tarballsDir): filePath = joinpath(tarballsDir, package.getTarballName()) try: verifyFile(filePath, package.fileLength, package.checksums) except IOError, ex: print >> sys.stderr, '%s corrupt: %s' % ( package.getTarballName(), ex ) sys.exit(1) def extractPackage(package, tarballsDir, sourcesDir, patchesDir): if not isdir(sourcesDir): makedirs(sourcesDir) sourceDirName = package.getSourceDirName() packageSrcDir = joinpath(sourcesDir, sourceDirName) if isdir(packageSrcDir): rmtree(packageSrcDir) extract( joinpath(tarballsDir, package.getTarballName()), sourcesDir, TopLevelDirRenamer(sourceDirName) ) diffPath = joinpath(patchesDir, sourceDirName + '.diff') if isfile(diffPath): for diff in Diff.load(diffPath): patch(diff, sourcesDir) print 'Patched:', diff.getPath() def fetchPackageSource(makeName, tarballsDir, sourcesDir, patchesDir): package = getPackage(makeName) downloadPackage(package, tarballsDir) verifyPackage(package, tarballsDir) extractPackage(package, tarballsDir, sourcesDir, patchesDir) def main(platform, tarballsDir, sourcesDir, patchesDir): if platform == 'android': fetchPackageSource('TCL_ANDROID', tarballsDir, sourcesDir, patchesDir) else: configuration = getConfiguration('3RD_STA') components = configuration.iterDesiredComponents() # Compute the set of all directly and indirectly required libraries, # then filter out system libraries. thirdPartyLibs = set( makeName for makeName in allDependencies(requiredLibrariesFor(components)) if not librariesByName[makeName].isSystemLibrary(platform) ) for makeName in sorted(thirdPartyLibs): fetchPackageSource(makeName, tarballsDir, sourcesDir, patchesDir) if __name__ == '__main__': if len(sys.argv) == 2: main( sys.argv[1], 'derived/3rdparty/download', 'derived/3rdparty/src', 'build/3rdparty' ) else: print >> sys.stderr, ( 'Usage: python thirdparty_download.py TARGET_OS' ) sys.exit(2) openmsx-0.10.0/build/packages.py0000644000175000017500000001614512262345041017302 0ustar manuelmanuel00000000000000from urlparse import urljoin class Package(object): '''Abstract base class for packages. ''' niceName = None sourceName = None @classmethod def getMakeName(cls): return cls.sourceName.upper() class DownloadablePackage(Package): '''Abstract base class for packages that can be downloaded. ''' downloadURL = None version = None fileLength = None checksums = None @classmethod def getSourceDirName(cls): '''Returns the desired name of the top-level source directory. This might not match the actual name inside the downloaded archive, but we can perform a rename on extraction to fix that. ''' return '%s-%s' % (cls.sourceName, cls.version) @classmethod def getTarballName(cls): return '%s-%s.tar.gz' % (cls.sourceName, cls.version) @classmethod def getURL(cls): return urljoin(cls.downloadURL + '/', cls.getTarballName()) class DirectX(DownloadablePackage): downloadURL = 'http://alleg.sourceforge.net/files' niceName = 'DirectX' sourceName = 'dx' version = '70' fileLength = 236675 checksums = { 'sha256': '59f489a7d9f51c70fe37fbb5a6225d4716a97ab774c58138f1dc4661a80356f0', } @classmethod def getMakeName(cls): return 'DIRECTX' @classmethod def getSourceDirName(cls): # Note: The tarball does not contain a source dir. # We only redefine this to keep the name of the install # timestamp the same. return '%s%s' % (cls.sourceName, cls.version) @classmethod def getTarballName(cls): return '%s%s_mgw.tar.gz' % (cls.sourceName, cls.version) class Expat(DownloadablePackage): downloadURL = 'http://downloads.sourceforge.net/expat' niceName = 'Expat' sourceName = 'expat' version = '2.0.1' fileLength = 446456 checksums = { 'sha256': '847660b4df86e707c9150e33cd8c25bc5cd828f708c7418e765e3e983a2e5e93', } class FreeType(DownloadablePackage): downloadURL = 'http://downloads.sourceforge.net/freetype' niceName = 'FreeType' sourceName = 'freetype' version = '2.4.12' fileLength = 2117909 checksums = { 'sha256': '9755806ff72cba095aad47dce6f0ad66bd60fee2a90323707d2cac5c526066f0', } class GLEW(DownloadablePackage): downloadURL = 'http://downloads.sourceforge.net/glew' niceName = 'GLEW' sourceName = 'glew' version = '1.9.0' fileLength = 544440 checksums = { 'sha256': '9b36530e414c95d6624be9d6815a5be1531d1986300ae5903f16977ab8aeb787', } @classmethod def getTarballName(cls): return '%s-%s.tgz' % (cls.sourceName, cls.version) class LibAO(DownloadablePackage): downloadURL = 'http://downloads.xiph.org/releases/ao' niceName = 'libao' sourceName = 'libao' version = '1.1.0' fileLength = 397102 checksums = { 'sha256': '29de5bb9b1726ba890455ef7e562d877df87811febb0d99ee69164b88c171bd4', } @classmethod def getMakeName(cls): return 'AO' class LibPNG(DownloadablePackage): downloadURL = 'http://downloads.sourceforge.net/libpng' niceName = 'libpng' sourceName = 'libpng' version = '1.2.50' fileLength = 826893 checksums = { 'sha256': '19f17cd49782fcec8df0f7d1b348448cc3f69ed7e2a59de24bc0907b907f1abc', } @classmethod def getMakeName(cls): return 'PNG' class LibXML2(DownloadablePackage): downloadURL = 'http://xmlsoft.org/sources' niceName = 'libxml2' sourceName = 'libxml2' version = '2.8.0' # 2.9.0 and 2.9.1 do not seem to compile... fileLength = 4915203 checksums = { 'sha256': 'f2e2d0e322685193d1affec83b21dc05d599e17a7306d7b90de95bb5b9ac622a', } @classmethod def getMakeName(cls): return 'XML' class OGG(DownloadablePackage): downloadURL = 'http://downloads.xiph.org/releases/ogg' niceName = 'libogg' sourceName = 'libogg' version = '1.3.0' fileLength = 425144 checksums = { 'sha256': 'a8de807631014615549d2356fd36641833b8288221cea214f8a72750efe93780', } @classmethod def getMakeName(cls): return 'OGG' class OpenGL(Package): niceName = 'OpenGL' sourceName = 'gl' class SDL(DownloadablePackage): downloadURL = 'http://www.libsdl.org/release' niceName = 'SDL' sourceName = 'SDL' version = '1.2.15' fileLength = 3920622 checksums = { 'sha256': 'd6d316a793e5e348155f0dd93b979798933fb98aa1edebcc108829d6474aad00', } class SDL_ttf(DownloadablePackage): downloadURL = 'http://www.libsdl.org/projects/SDL_ttf/release' niceName = 'SDL_ttf' sourceName = 'SDL_ttf' version = '2.0.11' fileLength = 4053686 checksums = { 'sha256': '724cd895ecf4da319a3ef164892b72078bd92632a5d812111261cde248ebcdb7', } class SQLite(DownloadablePackage): downloadURL = 'http://www.sqlite.org/' niceName = 'SQLite' sourceName = 'sqlite' version = '3.6.16' fileLength = 1353155 checksums = { 'sha256': 'f576c1be29726c03c079ac466095776b2c5b1ac8f996af1422b251855a0619a9', } @classmethod def getTarballName(cls): return 'sqlite-amalgamation-%s.tar.gz' % cls.version class TCL_ANDROID(DownloadablePackage): downloadURL = 'http://downloads.sourceforge.net/tcl' niceName = 'Tcl' sourceName = 'tcl' version = '8.5.11' fileLength = 4484001 checksums = { 'sha256': '8addc385fa6b5be4605e6d68fbdc4c0e674c5af1dc1c95ec5420390c4b08042a', } @classmethod def getMakeName(cls): return 'TCL_ANDROID' @classmethod def getSourceDirName(cls): return '%s%s' % (cls.sourceName, cls.version) @classmethod def getTarballName(cls): return '%s%s-src.tar.gz' % (cls.sourceName, cls.version) class TCL(DownloadablePackage): downloadURL = 'http://downloads.sourceforge.net/tcl' niceName = 'Tcl' sourceName = 'tcl' version = '8.5.14' fileLength = 4528533 checksums = { 'sha256': '7494e94f1e195a505c542a3c50e01589d2f8bfd19597382827a895fa1c471f2d', } @classmethod def getSourceDirName(cls): return '%s%s' % (cls.sourceName, cls.version) @classmethod def getTarballName(cls): return '%s%s-src.tar.gz' % (cls.sourceName, cls.version) class Theora(DownloadablePackage): downloadURL = 'http://downloads.xiph.org/releases/theora' niceName = 'libtheora' sourceName = 'libtheora' version = '1.1.1' fileLength = 2111877 checksums = { 'sha256': '40952956c47811928d1e7922cda3bc1f427eb75680c3c37249c91e949054916b', } @classmethod def getMakeName(cls): return 'THEORA' class Vorbis(DownloadablePackage): downloadURL = 'http://downloads.xiph.org/releases/vorbis' niceName = 'libvorbis' sourceName = 'libvorbis' version = '1.3.3' fileLength = 1592663 checksums = { 'sha256': '6d747efe7ac4ad249bf711527882cef79fb61d9194c45b5ca5498aa60f290762', } @classmethod def getMakeName(cls): return 'VORBIS' class ZLib(DownloadablePackage): downloadURL = 'http://downloads.sourceforge.net/libpng' niceName = 'zlib' sourceName = 'zlib' version = '1.2.8' fileLength = 571091 checksums = { 'sha256': '36658cb768a54c1d4dec43c3116c27ed893e88b02ecfcb44f2166f9c0b7f2a0d', } # Build a dictionary of packages using introspection. def _discoverPackages(localObjects): for obj in localObjects: if isinstance(obj, type) and issubclass(obj, Package): if not (obj is Package or obj is DownloadablePackage): yield obj.getMakeName(), obj _packagesByName = dict(_discoverPackages(locals().itervalues())) def getPackage(makeName): return _packagesByName[makeName] def iterDownloadablePackages(): for package in _packagesByName.itervalues(): if issubclass(package, DownloadablePackage): yield package openmsx-0.10.0/build/dist.py0000644000175000017500000000275412262345041016470 0ustar manuelmanuel00000000000000# Creates a source distribution package. from fileutils import installDirs, installTree, scanTree from version import getVersionedPackageName from os.path import isdir, islink, join as joinpath from shutil import rmtree import sys import tarfile def main(paths): versionedPackageName = getVersionedPackageName() distBase = 'derived/dist/' distPath = distBase + versionedPackageName + '/' if isdir(distPath): print 'Removing old distribution files...' if islink(distPath): # Note: Python 2.6 does this check and it seems like a sensible # precaution; since we support Python 2.5 as well we # explictly perform the check before calling rmtree(). raise OSError( 'Refusing to recursively delete symlinked directory "%s"' % distPath ) rmtree(distPath) print 'Gathering files for distribution...' def gather(): for path in paths: if isdir(path): for relpath in scanTree(path): yield joinpath(path, relpath) else: yield path installDirs(distPath) installTree('.', distPath, gather()) print 'Creating tarball...' # TODO: Try to generate the tarball directly from the provided file lists, # without creating a copy in the "dist" directory first. tar = tarfile.open(distBase + versionedPackageName + '.tar.gz', 'w:gz') try: tar.add(distPath, versionedPackageName) finally: tar.close() if __name__ == '__main__': if len(sys.argv) > 1: main(sys.argv[1 : ]) else: print >> sys.stderr, 'Usage: python dist.py paths' sys.exit(2) openmsx-0.10.0/build/flavour-debug.mk0000644000175000017500000000037412262345041020242 0ustar manuelmanuel00000000000000# Configuration for "debug" flavour: # Build with all debugging info, no optimisations. # Debug flags. CXXFLAGS+=-O0 -g -DDEBUG -D_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC \ -D_GLIBCPP_CONCEPT_CHECKS # Strip executable? OPENMSX_STRIP:=false openmsx-0.10.0/build/platform-pandora.mk0000644000175000017500000000052612262345041020745 0ustar manuelmanuel00000000000000# Configuration for Pandora. ifeq ($(OPENMSX_TARGET_CPU),arm) # Note: CXX is automatically set by the "setprj" command. # Target Cortex A8 CPU. TARGET_FLAGS+=-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp endif # Pandora is a Linux system. include build/platform-linux.mk # Identify the binary executable file. EXEEXT:=.bin openmsx-0.10.0/build/platform-maemo5.mk0000644000175000017500000000114312262345041020500 0ustar manuelmanuel00000000000000# Configuration for Maemo 5 devices. # Maemo is based on Linux. include build/platform-linux.mk # Workaround for SDL bug 586: the Matchbox2 window manager will not give input # events to our window if we set an icon, because SDL sets the wrong flags on # the icon. # http://bugzilla.libsdl.org/show_bug.cgi?id=586 # http://talk.maemo.org/showthread.php?t=31696 # This bug is fixed in SDL 1.2.14 (at least, I verified that the patch has been # applied), but Maemo 5 is using an older SDL. SET_WINDOW_ICON:=false # Link to X11 lib, Needed for the code that configures window composition. LINK_FLAGS+=-lX11 openmsx-0.10.0/build/main.mk0000644000175000017500000005622612262345041016433 0ustar manuelmanuel00000000000000# openMSX Build System # ==================== # # This is the home made build system for openMSX, which replaced the # autoconf/automake combo. # # Used a lot of ideas from Peter Miller's excellent paper # "Recursive Make Considered Harmful". # http://miller.emu.id.au/pmiller/books/rmch/ # TODO: # - Move calculation of CFLAGS and LDFLAGS to components2defs.py? # Python Interpreter # ================== # We need Python from the 2.x series, version 2.5 or higher. # Usually this executable is available as just "python", but on some systems # you might have to be more specific, for example "python2" or "python2.6". # Or if the Python interpreter is not in the search path, you can specify its # full path. ifeq ($(PYTHON),) PYTHON:=$(shell build/python-search.sh) ifeq ($(PYTHON),) $(error No suitable Python interpreter found. Please install Python version 2.x where x >= 5. If your Python interpreter is installed in a non-standard location, please set the environment variable PYTHON to the full path of the interpreter binary.) endif endif $(info Using Python: $(PYTHON)) # Delete on Error # =============== # Delete output if rule fails. # This is a flag that applies to all rules. .DELETE_ON_ERROR: # Logical Targets # =============== ifneq ($(words $(MAKECMDGOALS)),1) $(error main.mk can only handle once goal at a time) endif # TODO: "dist" and "createsubs" are missing # TODO: more missing? # Logical targets which require dependency files. DEPEND_TARGETS:=all default install run bindist # Logical targets which do not require dependency files. NODEPEND_TARGETS:=clean config probe 3rdparty staticbindist # Mark all logical targets as such. .PHONY: $(DEPEND_TARGETS) $(NODEPEND_TARGETS) # Settings # ======== # # There are platform specific settings and flavour specific settings. # platform: architecture, OS # flavour: optimisation levels, debugging, profiling # Function to check a variable has been defined and has a non-empty value. # Usage: $(call DEFCHECK,VARIABLE_NAME) DEFCHECK=$(strip \ $(if $(filter _undefined,_$(origin $(1))), \ $(error Variable $(1) is undefined) ) \ ) # Function to check a boolean variable has value "true" or "false". # Usage: $(call BOOLCHECK,VARIABLE_NAME) BOOLCHECK=$(DEFCHECK)$(strip \ $(if $(filter-out _true _false,_$($(1))), \ $(error Value of $(1) ("$($(1))") should be "true" or "false") ) \ ) # Will be added to by platform specific Makefile, by flavour specific Makefile # and by this Makefile. # Note: CXXFLAGS is overridable from the command line; COMPILE_FLAGS is not. # We use CXXFLAGS for flavour specific flags and COMPILE_FLAGS for # platform specific flags. CXXFLAGS:= COMPILE_FLAGS:= COMPILE_ENV:= # Note: LDFLAGS are passed to the linker itself, LINK_FLAGS are passed to the # compiler in the link phase. LDFLAGS:= LINK_FLAGS:= LINK_ENV:= # Flags that specify the target platform. # These should be inherited by the 3rd party libs Makefile. TARGET_FLAGS:= # Customisation # ============= include build/custom.mk $(call DEFCHECK,INSTALL_BASE) $(call BOOLCHECK,VERSION_EXEC) $(call BOOLCHECK,SYMLINK_FOR_BINARY) $(call BOOLCHECK,INSTALL_CONTRIB) # Platforms # ========= # Note: # A platform currently specifies both the host platform (performing the build) # and the target platform (running the created binary). When we have real # experience with cross-compilation, a more sophisticated system can be # designed. LINK_MODE:=$(if $(filter true,$(3RDPARTY_FLAG)),3RD_STA,SYS_DYN) # Do not perform autodetection if platform was specified by the user. ifneq ($(filter undefined,$(origin OPENMSX_TARGET_CPU) $(origin OPENMSX_TARGET_OS)),) DETECTSYS_SCRIPT:=build/detectsys.py LOCAL_PLATFORM:=$(shell $(PYTHON) $(DETECTSYS_SCRIPT)) ifeq ($(LOCAL_PLATFORM),) $(error No platform specified using OPENMSX_TARGET_CPU and OPENMSX_TARGET_OS and autodetection of local platform failed) endif OPENMSX_TARGET_CPU:=$(word 1,$(subst -, ,$(LOCAL_PLATFORM))) OPENMSX_TARGET_OS:=$(word 2,$(subst -, ,$(LOCAL_PLATFORM))) endif # OPENMSX_TARGET_CPU && OPENMSX_TARGET_OS PLATFORM:= ifneq ($(origin OPENMSX_TARGET_OS),undefined) ifneq ($(origin OPENMSX_TARGET_CPU),undefined) PLATFORM:=$(OPENMSX_TARGET_CPU)-$(OPENMSX_TARGET_OS) endif endif # Ignore rest of Makefile if autodetection was not performed yet. # Note that the include above will force a reload of the Makefile. ifneq ($(PLATFORM),) # List of CPUs to compile for. ifeq ($(OPENMSX_TARGET_CPU),univ) CPU_LIST:=x86 x86_64 else CPU_LIST:=$(OPENMSX_TARGET_CPU) endif # Default flavour. $(call DEFCHECK,OPENMSX_TARGET_CPU) ifeq ($(OPENMSX_TARGET_CPU),x86) ifeq ($(filter darwin%,$(OPENMSX_TARGET_OS)),) # To run openMSX with decent speed, at least a Pentium 2 class machine # is needed, so let's optimise for that. OPENMSX_FLAVOUR?=i686 else # The system headers of OS X use SSE features, which are not available on # i686, so we only use the generic optimisation flags instead. OPENMSX_FLAVOUR?=opt endif else ifeq ($(OPENMSX_TARGET_CPU),ppc) OPENMSX_FLAVOUR?=ppc else ifeq ($(OPENMSX_TARGET_CPU),m68k) OPENMSX_FLAVOUR?=m68k else OPENMSX_FLAVOUR?=opt endif endif endif # Load OS specific settings. $(call DEFCHECK,OPENMSX_TARGET_OS) include build/platform-$(OPENMSX_TARGET_OS).mk # Check that all expected variables were defined by OS specific Makefile: # - library file name extension $(call DEFCHECK,LIBRARYEXT) # - executable file name extension $(call DEFCHECK,EXEEXT) # - platform supports symlinks? $(call BOOLCHECK,USE_SYMLINK) ifneq ($(OPENMSX_TARGET_CPU),univ) # Get CPU specific flags. TARGET_FLAGS+=$(shell $(PYTHON) build/cpu2flags.py $(OPENMSX_TARGET_CPU)) endif # Flavours # ======== ifneq ($(OPENMSX_TARGET_CPU),univ) # Load flavour specific settings. include build/flavour-$(OPENMSX_FLAVOUR).mk endif # Paths # ===== BUILD_PATH:=derived/$(PLATFORM)-$(OPENMSX_FLAVOUR) ifeq ($(3RDPARTY_FLAG),true) BUILD_PATH:=$(BUILD_PATH)-3rd endif # Own build of 3rd party libs. ifeq ($(3RDPARTY_FLAG),true) 3RDPARTY_INSTALL_DIR:=$(BUILD_PATH)/3rdparty/install endif SOURCES_PATH:=src BINARY_PATH:=$(BUILD_PATH)/bin BINARY_FILE:=openmsx$(EXEEXT) BINDIST_DIR:=$(BUILD_PATH)/bindist BINDIST_PACKAGE:= ifeq ($(VERSION_EXEC),true) REVISION:=$(shell PYTHONPATH=build $(PYTHON) -c \ "import version; print version.extractRevisionString()" \ ) BINARY_FULL:=$(BINARY_PATH)/openmsx-$(REVISION)$(EXEEXT) else BINARY_FULL:=$(BINARY_PATH)/$(BINARY_FILE) endif LIBRARY_FILE:=openmsx$(LIBRARYEXT) LIBRARY_PATH:=$(BUILD_PATH)/lib LIBRARY_FULL:=$(LIBRARY_PATH)/$(LIBRARY_FILE) BUILDINFO_SCRIPT:=build/buildinfo2code.py CONFIG_HEADER:=$(BUILD_PATH)/config/build-info.hh PROBE_SCRIPT:=build/probe.py PROBE_MAKE:=$(BUILD_PATH)/config/probed_defs.mk VERSION_SCRIPT:=build/version2code.py VERSION_HEADER:=$(BUILD_PATH)/config/Version.ii COMPONENTS_HEADER_SCRIPT:=build/components2code.py COMPONENTS_DEFS_SCRIPT:=build/components2defs.py COMPONENTS_HEADER:=$(BUILD_PATH)/config/components.hh COMPONENTS_DEFS:=$(BUILD_PATH)/config/components_defs.mk GENERATED_HEADERS:=$(VERSION_HEADER) $(CONFIG_HEADER) $(COMPONENTS_HEADER) # Configuration # ============= ifneq ($(OPENMSX_TARGET_CPU),univ) ifneq ($(filter $(DEPEND_TARGETS),$(MAKECMDGOALS)),) -include $(PROBE_MAKE) -include $(COMPONENTS_DEFS) endif # goal requires dependencies endif # universal binary # Filesets # ======== # Force evaluation upon assignment. SOURCES_FULL:= HEADERS_FULL:= DIST_FULL:= # Include root node. CURDIR:= include node.mk # Remove "./" in front of file names. # It can cause trouble because Make removes it automatically in rules. SOURCES_FULL:=$(SOURCES_FULL:./%=%) HEADERS_FULL:=$(HEADERS_FULL:./%=%) DIST_FULL:=$(DIST_FULL:./%=%) # Apply subset to sources list. SOURCES_FULL:=$(filter $(SOURCES_PATH)/$(OPENMSX_SUBSET)%,$(SOURCES_FULL)) ifeq ($(SOURCES_FULL),) $(error Sources list empty $(if \ $(OPENMSX_SUBSET),after applying subset "$(OPENMSX_SUBSET)*")) endif # Sanity check: only .cc files are allowed in sources list, # because we don't have any way to build other sources. NON_CC_SOURCES:=$(filter-out %.cc,$(SOURCES_FULL)) ifneq ($(NON_CC_SOURCES),) $(error The following sources files do not have a .cc extension: \ $(NON_CC_SOURCES)) endif SOURCES:=$(SOURCES_FULL:$(SOURCES_PATH)/%.cc=%) HEADERS:=$(HEADERS_FULL:$(SOURCES_PATH)/%=%) DEPEND_PATH:=$(BUILD_PATH)/dep DEPEND_FULL:=$(addsuffix .d,$(addprefix $(DEPEND_PATH)/,$(SOURCES))) OBJECTS_PATH:=$(BUILD_PATH)/obj OBJECTS_FULL:=$(addsuffix .o,$(addprefix $(OBJECTS_PATH)/,$(SOURCES))) ifeq ($(OPENMSX_TARGET_OS),mingw32) RESOURCE_SRC:=src/resource/openmsx.rc RESOURCE_OBJ:=$(OBJECTS_PATH)/resources.o RESOURCE_SCRIPT:=build/win_resource.py RESOURCE_HEADER:=$(BUILD_PATH)/config/resource-info.h else RESOURCE_OBJ:= endif # Compiler and Flags # ================== COMPILE_FLAGS+=$(TARGET_FLAGS) LINK_FLAGS+=$(TARGET_FLAGS) # Determine compiler. CXX?=g++ WINDRES?=windres DEPEND_FLAGS:= ifneq ($(filter %clang++,$(CXX))$(filter clang++%,$(CXX)),) # Enable C++11 COMPILE_FLAGS+=-std=c++11 # Clang does support -Wunused-macros, but it triggers on SDL's headers, # causing way too many false positives that we cannot fix. COMPILE_FLAGS+=-Wall -Wextra -Wundef # TODO: Remove the overloading from the code instead. COMPILE_FLAGS+=-Wno-overloaded-virtual CC:=$(subst clang++,clang,$(CXX)) LD:=ld DEPEND_FLAGS+=-MP else ifneq ($(filter %g++,$(CXX))$(filter g++%,$(CXX))$(findstring /g++-,$(CXX)),) # Generic compilation flags. COMPILE_FLAGS+=-pipe # Enable C++11 COMPILE_FLAGS+=-std=gnu++0x # Stricter warning and error reporting. COMPILE_FLAGS+=-Wall -Wextra -Wundef -Wunused-macros # -Wdouble-promotion <-- this is useful, but only support from gcc-4.6 # Flag that is not accepted by old GCC versions. COMPILE_FLAGS+=$(shell \ echo | $(CXX) -E -Wno-missing-field-initializers - >/dev/null 2>&1 \ && echo -Wno-missing-field-initializers \ ) # -Wzero-as-null-pointer-constant is available from gcc-4.7 COMPILE_FLAGS+=$(shell \ echo | $(CXX) -E -Wzero-as-null-pointer-constant - >/dev/null 2>&1 \ && echo -Wzero-as-null-pointer-constant \ ) # Empty definition of used headers, so header removal doesn't break things. DEPEND_FLAGS+=-MP # Plain C compiler, for the 3rd party libs. CC:=$(subst g++,gcc,$(CXX)) # Guess the name of the linker. LD:=$(subst g++,ld,$(CXX:g++%=g++)) else ifneq ($(filter %gcc,$(CXX))$(filter gcc%,$(CXX)),) $(error Set CXX to your "g++" executable instead of "gcc") endif ifneq ($(filter %icc,$(CXX)),) # Report all errors, warnings and remarks, except the following remarks: # (on the openmsx-devel list these were discussed and it was decided to # disable them since fixing them would not improve code quality) # 177: "handler parameter "e" was declared but never referenced" # 185: "dynamic initialization in unreachable code" # 271: "trailing comma is nonstandard" # 279: "controlling expression is constant" # 383: "value copied to temporary, reference to temporary used" # 869: "parameter [name] was never referenced" # 981: "operands are evaluated in unspecified order" COMPILE_FLAGS+=-Wall -wd177,185,271,279,383,869,981 # Temporarily disabled remarks: (may be re-enabled some time) # 111: "statement is unreachable" # Occurs in template where code is unreachable for some expansions # but not for others. # 444: "destructor for base class [class] is not virtual" # Sometimes issued incorrectly. # Reported to Intel: issue number 221909. # 530: "inline function [name] cannot be explicitly instantiated" # Issued when explicitly instantiating template classes with inline # methods. Needs more investigation / discussion. # 810: "conversion from [larger type] to [smaller type] may lose # significant bits" # Many instances not fixed yet, but should be fixed eventually. # 1125: "function [f1] is hidden by [f2] -- virtual function override # intended?" # Occurs lots of times on Command class. I don't understand why it # thinks hiding is occurring there. # 1469: ""cc" clobber ignored" # Seems to be caused by glibc headers. COMPILE_FLAGS+=-wd111,444,530,810,1125,1469 # Plain C compiler, for the 3rd party libs. CC:=icc else $(warning Unsupported compiler: $(CXX), please update Makefile) endif endif endif # Check if ccache usage was requested ifeq ($(USE_CCACHE),true) override CC:=ccache $(CC) override CXX:=ccache $(CXX) endif # Strip binary? OPENMSX_STRIP?=false $(call BOOLCHECK,OPENMSX_STRIP) STRIP_SEPARATE:=false ifeq ($(OPENMSX_STRIP),true) ifeq ($(OPENMSX_TARGET_OS),darwin) # Current (mid-2006) GCC 4.x for OS X will strip too many symbols, # resulting in a binary that cannot run. # However, the separate "strip" tool does work correctly. STRIP_SEPARATE:=true else # Tell GCC to produce a stripped binary. LINK_FLAGS+=-s endif endif # Determine common compile flags. INCLUDE_INTERNAL:=$(sort $(foreach header,$(HEADERS_FULL),$(patsubst %/,%,$(dir $(header))))) INCLUDE_INTERNAL+=$(BUILD_PATH)/config COMPILE_FLAGS+=$(addprefix -I,$(INCLUDE_INTERNAL)) # Determine common link flags. LINK_FLAGS_PREFIX:=-Wl, LINK_FLAGS+=$(addprefix $(LINK_FLAGS_PREFIX),$(LDFLAGS)) # Determine component specific compile and link flags. ifeq ($(COMPONENT_CORE),true) COMPILE_FLAGS+=$(foreach lib,$(CORE_LIBS),$($(lib)_CFLAGS)) LINK_FLAGS+=$(foreach lib,$(CORE_LIBS),$($(lib)_LDFLAGS)) endif ifeq ($(COMPONENT_GL),true) COMPILE_FLAGS+=$(GL_CFLAGS) $(GLEW_CFLAGS) $(GLEW_GL_CFLAGS) LINK_FLAGS+=$(GL_LDFLAGS) $(GLEW_LDFLAGS) endif ifeq ($(COMPONENT_LASERDISC),true) COMPILE_FLAGS+=$(OGG_CFLAGS) $(VORBIS_CFLAGS) $(THEORA_CFLAGS) LINK_FLAGS+=$(OGG_LDFLAGS) $(VORBIS_LDFLAGS) $(THEORA_LDFLAGS) endif ifeq ($(COMPONENT_AO),true) COMPILE_FLAGS+=$(AO_CFLAGS) LINK_FLAGS+=$(AO_LDFLAGS) endif # Build Rules # =========== # Force a probe if "probe" target is passed explicitly. ifeq ($(MAKECMDGOALS),probe) probe: $(PROBE_MAKE) .PHONY: $(PROBE_MAKE) endif # Probe for headers and functions. $(PROBE_MAKE): $(PROBE_SCRIPT) build/custom.mk \ build/systemfuncs2code.py build/systemfuncs.py @$(PYTHON) $(PROBE_SCRIPT) \ "$(COMPILE_ENV) $(CXX) $(TARGET_FLAGS)" \ $(@D) $(OPENMSX_TARGET_OS) $(LINK_MODE) "$(3RDPARTY_INSTALL_DIR)" @touch $@ # Generate configuration header. # TODO: One platform file may include another, so the real solution would be # for the Python script to write dependency info. $(CONFIG_HEADER): $(BUILDINFO_SCRIPT) \ build/custom.mk build/platform-$(OPENMSX_TARGET_OS).mk @$(PYTHON) $(BUILDINFO_SCRIPT) $@ \ $(OPENMSX_TARGET_OS) $(OPENMSX_TARGET_CPU) $(OPENMSX_FLAVOUR) \ $(INSTALL_SHARE_DIR) @touch $@ # Generate version header. .PHONY: forceversionextraction forceversionextraction: $(VERSION_HEADER): forceversionextraction @$(PYTHON) $(VERSION_SCRIPT) $@ # Generate components header. $(COMPONENTS_HEADER): $(COMPONENTS_HEADER_SCRIPT) $(PROBE_MAKE) \ build/components.py @$(PYTHON) $(COMPONENTS_HEADER_SCRIPT) $@ $(PROBE_MAKE) @touch $@ # Generate components Makefile. $(COMPONENTS_DEFS): $(COMPONENTS_DEFS_SCRIPT) $(PROBE_MAKE) \ build/components.py @$(PYTHON) $(COMPONENTS_DEFS_SCRIPT) $@ $(PROBE_MAKE) @touch $@ # Default target. ifeq ($(OPENMSX_TARGET_OS),darwin) all: app else ifeq ($(OPENMSX_TARGET_OS),android) all: $(LIBRARY_FULL) else all: $(BINARY_FULL) endif endif # This is a workaround for the lack of order-only dependencies in GNU Make # versions older than 3.80 (for example Mac OS X 10.3 still ships with 3.79). # It creates a dummy file, which is never modified after its initial creation. # If a rule that produces a file does not modify that file, Make considers the # target to be up-to-date. That way, the targets "init-dummy-file" depends on # will always be checked before compilation, but they will not cause all object # files to be considered outdated. INIT_DUMMY_FILE:=$(BUILD_PATH)/config/init-dummy-file $(INIT_DUMMY_FILE): config $(GENERATED_HEADERS) @touch -a $@ # Print configuration. config: ifeq ($(COMPONENT_CORE),false) # Do not build if core component dependencies are not met. @echo 'Cannot build openMSX because essential libraries are unavailable.' @echo 'Please install the needed libraries and their header files and rerun "configure"' @false endif @echo "Build configuration:" @echo " Platform: $(PLATFORM)" @echo " Flavour: $(OPENMSX_FLAVOUR)" @echo " Compiler: $(CXX)" @echo " Subset: $(if $(OPENMSX_SUBSET),$(OPENMSX_SUBSET)*,full build)" # Include dependency files. ifneq ($(filter $(DEPEND_TARGETS),$(MAKECMDGOALS)),) -include $(DEPEND_FULL) endif # Clean up build tree of current flavour. clean: @echo "Cleaning up..." @rm -rf $(BUILD_PATH) # Create Makefiles in source subdirectories, to conveniently build a subset. ifeq ($(MAKECMDGOALS),createsubs) # Function that concatenates list items to form a single string. # Usage: $(call JOIN,TEXT) JOIN=$(if $(1),$(firstword $(1))$(call JOIN,$(wordlist 2,999999,$(1))),) RELPATH=$(call JOIN,$(patsubst %,../,$(subst /, ,$(@:%/GNUmakefile=%)))) SUB_MAKEFILES:=$(addsuffix GNUmakefile,$(sort $(dir $(SOURCES_FULL)))) createsubs: $(SUB_MAKEFILES) $(SUB_MAKEFILES): @echo "Creating $@..." @echo "export OPENMSX_SUBSET=$(@:$(SOURCES_PATH)/%GNUmakefile=%)" > $@ @echo "all:" >> $@ @echo " @\$$(MAKE) -C $(RELPATH) -f build/main.mk all" >> $@ # Force re-creation every time this target is run. .PHONY: $(SUB_MAKEFILES) endif # Compile and generate dependency files in one go. DEPEND_SUBST=$(patsubst $(SOURCES_PATH)/%.cc,$(DEPEND_PATH)/%.d,$<) $(OBJECTS_FULL): $(INIT_DUMMY_FILE) $(OBJECTS_FULL): $(OBJECTS_PATH)/%.o: $(SOURCES_PATH)/%.cc $(DEPEND_PATH)/%.d @echo "Compiling $(patsubst $(SOURCES_PATH)/%,%,$<)..." @mkdir -p $(@D) @mkdir -p $(patsubst $(OBJECTS_PATH)%,$(DEPEND_PATH)%,$(@D)) @$(COMPILE_ENV) $(CXX) \ $(DEPEND_FLAGS) -MMD -MF $(DEPEND_SUBST) \ -o $@ $(CXXFLAGS) $(COMPILE_FLAGS) -c $< @touch $@ # Force .o file to be newer than .d file. # Generate dependencies that do not exist yet. # This is only in case some .d files have been deleted; # in normal operation this rule is never triggered. $(DEPEND_FULL): # Windows resources that are added to the executable. ifeq ($(OPENMSX_TARGET_OS),mingw32) $(RESOURCE_HEADER): $(INIT_DUMMY_FILE) forceversionextraction @$(PYTHON) $(RESOURCE_SCRIPT) $@ $(RESOURCE_OBJ): $(RESOURCE_SRC) $(RESOURCE_HEADER) @echo "Compiling resources..." @mkdir -p $(@D) @$(WINDRES) $(addprefix --include-dir=,$(^D)) -o $@ -i $< endif # Link executable. ifeq ($(OPENMSX_TARGET_CPU),univ) BINARY_FOR_CPU=$(BINARY_FULL:derived/univ-%=derived/$(1)-%) SINGLE_CPU_BINARIES=$(foreach CPU,$(CPU_LIST),$(call BINARY_FOR_CPU,$(CPU))) .PHONY: $(SINGLE_CPU_BINARIES) $(SINGLE_CPU_BINARIES): @echo "Start compile for $(firstword $(subst -, ,$(@:derived/%=%))) CPU..." @$(MAKE) -f build/main.mk all \ OPENMSX_TARGET_CPU=$(firstword $(subst -, ,$(@:derived/%=%))) \ OPENMSX_TARGET_OS=$(OPENMSX_TARGET_OS) \ OPENMSX_FLAVOUR=$(OPENMSX_FLAVOUR) \ 3RDPARTY_FLAG=$(3RDPARTY_FLAG) \ PYTHON=$(PYTHON) @echo "Finished compile for $(firstword $(subst -, ,$(@:derived/%=%))) CPU." $(BINARY_FULL): $(SINGLE_CPU_BINARIES) @mkdir -p $(@D) @lipo -create $^ -output $@ else $(BINARY_FULL): $(OBJECTS_FULL) $(RESOURCE_OBJ) ifeq ($(OPENMSX_SUBSET),) @echo "Linking $(notdir $@)..." @mkdir -p $(@D) @+$(LINK_ENV) $(CXX) -o $@ $(CXXFLAGS) $^ $(LINK_FLAGS) ifeq ($(STRIP_SEPARATE),true) @echo "Stripping $(notdir $@)..." @strip $@ endif ifeq ($(USE_SYMLINK),true) @ln -sf $(@:derived/%=%) derived/$(BINARY_FILE) else @cp $@ derived/$(BINARY_FILE) endif else @echo "Not linking $(notdir $@) because only a subset was built." endif # subset endif # universal binary $(LIBRARY_FULL): $(OBJECTS_FULL) $(RESOURCE_OBJ) @echo "Linking $(notdir $@)..." @mkdir -p $(@D) @$(LINK_ENV) $(CXX) -o $@ $(CXXFLAGS) $^ $(LINK_FLAGS) # Run executable. run: all @echo "Running $(notdir $(BINARY_FULL))..." @$(BINARY_FULL) # Installation and Binary Packaging # ================================= ifneq ($(filter $(MAKECMDGOALS),bindist)$(filter $(OPENMSX_TARGET_OS),darwin),) # Create binary distribution directory. BINDIST_DIR:=$(BUILD_PATH)/bindist BINDIST_PACKAGE:= # Override install locations. INSTALL_ROOT:=$(BINDIST_DIR)/install ifeq ($(OPENMSX_TARGET_OS),mingw32) # In Windows the "share" dir is expected at the same level as the executable, # so do not put the executable in "bin". INSTALL_BINARY_DIR:=$(INSTALL_ROOT) else INSTALL_BINARY_DIR:=$(INSTALL_ROOT)/bin endif INSTALL_SHARE_DIR:=$(INSTALL_ROOT)/share INSTALL_DOC_DIR:=$(INSTALL_ROOT)/doc # C-BIOS should be included. INSTALL_CONTRIB:=true # Do not display header and post-install instructions. INSTALL_VERBOSE:=false .PHONY: bindist bindistclean bindist: install # Force removal of old destination dir before installing to new dir. install: bindistclean bindistclean: $(BINARY_FULL) @echo "Removing any old binary package..." @rm -rf $(BINDIST_DIR) @$(if $(BINDIST_PACKAGE),rm -f $(BINDIST_PACKAGE),) @echo "Creating binary package:" endif ifeq ($(OPENMSX_TARGET_OS),darwin) # Application directory for Darwin. # This handles the "bindist" target, but can also be used with the "install" # target to create an app folder but no DMG. include build/package-darwin/app.mk else ifeq ($(OPENMSX_TARGET_OS),dingux) # ZIP file package for Dingux. include build/package-dingux/zip.mk else # Note: Use OPENMSX_INSTALL only to create binary packages. # To change installation dir for actual installations, edit "custom.mk". OPENMSX_INSTALL?=$(INSTALL_BASE) # Allow full customization of locations, used by Debian packaging. INSTALL_BINARY_DIR?=$(OPENMSX_INSTALL)/bin INSTALL_SHARE_DIR?=$(OPENMSX_INSTALL)/share INSTALL_DOC_DIR?=$(OPENMSX_INSTALL)/doc INSTALL_VERBOSE?=true endif endif # DESTDIR is a convention shared by at least GNU and FreeBSD to specify a path # prefix that will be used for all installed files. install: $(BINARY_FULL) @$(PYTHON) build/install.py "$(DESTDIR)" \ $(INSTALL_BINARY_DIR) $(INSTALL_SHARE_DIR) $(INSTALL_DOC_DIR) \ $(BINARY_FULL) $(OPENMSX_TARGET_OS) \ $(INSTALL_VERBOSE) $(INSTALL_CONTRIB) # Source Packaging # ================ dist: @$(PYTHON) build/dist.py $(DIST_FULL) $(HEADERS_FULL) $(SOURCES_FULL) # Binary Packaging Using 3rd Party Libraries # ========================================== .PHONY: $(addprefix 3rdparty-,$(CPU_LIST)) run-3rdparty 3rdparty: $(addprefix 3rdparty-,$(CPU_LIST)) # Recursive invocation for different CPUs. # This is used even when building for a single CPU, since the OS and flavour # can differ. $(addprefix 3rdparty-,$(CPU_LIST)): $(MAKE) -f build/main.mk run-3rdparty \ OPENMSX_TARGET_CPU=$(@:3rdparty-%=%) \ OPENMSX_TARGET_OS=$(OPENMSX_TARGET_OS) \ OPENMSX_FLAVOUR=$(OPENMSX_FLAVOUR) \ 3RDPARTY_FLAG=true \ PYTHON=$(PYTHON) # Call third party Makefile with the right arguments. # This is an internal target, users should select "3rdparty" instead. run-3rdparty: $(MAKE) -f build/3rdparty.mk \ BUILD_PATH=$(BUILD_PATH)/3rdparty \ OPENMSX_TARGET_CPU=$(OPENMSX_TARGET_CPU) \ OPENMSX_TARGET_OS=$(OPENMSX_TARGET_OS) \ _CC=$(CC) _CFLAGS="$(TARGET_FLAGS) $(CXXFLAGS)" \ _LD=$(LD) _LDFLAGS="$(TARGET_FLAGS)" \ LINK_MODE=$(LINK_MODE) $(COMPILE_ENV) \ PYTHON=$(PYTHON) staticbindist: 3rdparty $(MAKE) -f build/main.mk bindist \ OPENMSX_TARGET_CPU=$(OPENMSX_TARGET_CPU) \ OPENMSX_TARGET_OS=$(OPENMSX_TARGET_OS) \ OPENMSX_FLAVOUR=$(OPENMSX_FLAVOUR) \ 3RDPARTY_FLAG=true \ PYTHON=$(PYTHON) endif # PLATFORM openmsx-0.10.0/build/node-start.mk0000644000175000017500000000100712262345041017552 0ustar manuelmanuel00000000000000# Should be included at the start of each node.mk file. # Get name of current directory. SUBDIR:=$(firstword $(SUBDIRSTACK)) SUBDIRSTACK:=$(wordlist 2,$(words $(SUBDIRSTACK)),$(SUBDIRSTACK)) # Push current directory on directory stack. DIRSTACK:=$(CURDIR) $(DIRSTACK) CURDIR:=$(CURDIR)$(SUBDIR) # Initialise node vars with empty value. SUBDIRS:= DIST:= SRC_HDR:= SRC_ONLY:= HDR_ONLY:= SRC_HDR_:= SRC_ONLY_:= HDR_ONLY_:= SRC_HDR_true:= SRC_ONLY_true:= HDR_ONLY_true:= SRC_HDR_false:= SRC_ONLY_false:= HDR_ONLY_false:= openmsx-0.10.0/build/probe.py0000644000175000017500000003046412262345041016633 0ustar manuelmanuel00000000000000# Replacement for autoconf. # Performs some test compiles, to check for headers and functions. # It does not execute anything it builds, making it friendly for cross compiles. from compilers import CompileCommand, LinkCommand from components import iterComponents, requiredLibrariesFor from configurations import getConfiguration from executils import captureStdout, shjoin from itertools import chain from libraries import librariesByName from makeutils import extractMakeVariables, parseBool from outpututils import rewriteIfChanged from packages import getPackage from systemfuncs import systemFunctions from systemfuncs2code import iterSystemFuncsHeader from os import environ, makedirs, remove from os.path import isdir, isfile, pathsep from shlex import split as shsplit import sys def resolve(log, expr): if expr is None: return '' # TODO: Since for example "sdl-config" is used in more than one # CFLAGS definition, it will be executed multiple times. try: return normalizeWhitespace(evaluateBackticks(log, expr)) except IOError: # Executing a lib-config script is expected to fail if the # script is not installed. # TODO: Report this explicitly in the probe results table. return '' def writeFile(path, lines): out = open(path, 'w') try: for line in lines: print >> out, line finally: out.close() def tryCompile(log, compileCommand, sourcePath, lines): '''Write the program defined by "lines" to a text file specified by "path" and try to compile it. Returns True iff compilation succeeded. ''' assert sourcePath.endswith('.cc') objectPath = sourcePath[ : -3] + '.o' writeFile(sourcePath, lines) try: return compileCommand.compile(log, sourcePath, objectPath) finally: remove(sourcePath) if isfile(objectPath): remove(objectPath) def checkCompiler(log, compileCommand, outDir): '''Checks whether compiler can compile anything at all. Returns True iff the compiler works. ''' def hello(): # The most famous program. yield '#include ' yield 'int main(int argc, char** argv) {' yield ' std::cout << "Hello World!" << std::endl;' yield ' return 0;' yield '}' return tryCompile(log, compileCommand, outDir + '/hello.cc', hello()) def checkFunc(log, compileCommand, outDir, checkName, funcName, headers): '''Checks whether the given function is declared by the given headers. Returns True iff the function is declared. ''' def takeFuncAddr(): # Try to include the necessary headers and get the function address. for header in headers: yield '#include %s' % header yield 'void (*f)() = reinterpret_cast(%s);' % funcName return tryCompile( log, compileCommand, outDir + '/' + checkName + '.cc', takeFuncAddr() ) def evaluateBackticks(log, expression): parts = [] index = 0 while True: start = expression.find('`', index) if start == -1: parts.append(expression[index : ]) break end = expression.find('`', start + 1) if end == -1: raise ValueError('Unmatched backtick: %s' % expression) parts.append(expression[index : start]) command = expression[start + 1 : end].strip() result = captureStdout(log, command) if result is None: raise IOError('Backtick evaluation failed; see log') parts.append(result) index = end + 1 return ''.join(parts) def normalizeWhitespace(expression): return shjoin(shsplit(expression)) class TargetSystem(object): def __init__( self, log, logPath, compileCommandStr, outDir, platform, distroRoot, configuration ): '''Create empty log and result files. ''' self.log = log self.logPath = logPath self.compileCommandStr = compileCommandStr self.outDir = outDir self.platform = platform self.distroRoot = distroRoot self.configuration = configuration self.outMakePath = outDir + '/probed_defs.mk' self.outHeaderPath = outDir + '/systemfuncs.hh' self.outVars = {} self.functionResults = {} self.typeTraitsResult = None self.libraries = sorted(requiredLibrariesFor( configuration.iterDesiredComponents() )) def checkAll(self): '''Run all probes. ''' self.hello() for func in systemFunctions: self.checkFunc(func) for library in self.libraries: self.checkLibrary(library) def writeAll(self): def iterVars(): yield '# Automatically generated by build system.' yield '# Non-empty value means found, empty means not found.' for library in self.libraries: for name in ( 'HAVE_%s_H' % library, 'HAVE_%s_LIB' % library, '%s_CFLAGS' % library, '%s_LDFLAGS' % library, ): yield '%s:=%s' % (name, self.outVars[name]) rewriteIfChanged(self.outMakePath, iterVars()) rewriteIfChanged( self.outHeaderPath, iterSystemFuncsHeader(self.functionResults), ) def printResults(self): for line in iterProbeResults( self.outVars, self.configuration, self.logPath ): print line def everything(self): self.checkAll() self.writeAll() self.printResults() def hello(self): '''Check compiler with the most famous program. ''' compileCommand = CompileCommand.fromLine(self.compileCommandStr, '') ok = checkCompiler(self.log, compileCommand, self.outDir) print >> self.log, 'Compiler %s: %s' % ( 'works' if ok else 'broken', compileCommand ) self.outVars['COMPILER'] = str(ok).lower() def checkFunc(self, func): '''Probe for function. ''' compileCommand = CompileCommand.fromLine(self.compileCommandStr, '') ok = checkFunc( self.log, compileCommand, self.outDir, func.name, func.getFunctionName(), func.iterHeaders(self.platform) ) print >> self.log, '%s function: %s' % ( 'Found' if ok else 'Missing', func.getFunctionName() ) self.functionResults[func.getMakeName()] = ok def checkLibrary(self, makeName): library = librariesByName[makeName] cflags = resolve( self.log, library.getCompileFlags( self.platform, self.configuration.linkStatic(), self.distroRoot ) ) ldflags = resolve( self.log, library.getLinkFlags( self.platform, self.configuration.linkStatic(), self.distroRoot ) ) compileCommand = CompileCommand.fromLine(self.compileCommandStr, cflags) linkCommand = LinkCommand.fromLine(self.compileCommandStr, ldflags) self.outVars['%s_CFLAGS' % makeName] = cflags self.outVars['%s_LDFLAGS' % makeName] = ldflags sourcePath = self.outDir + '/' + makeName + '.cc' objectPath = self.outDir + '/' + makeName + '.o' binaryPath = self.outDir + '/' + makeName + '.bin' if self.platform == 'android': binaryPath = self.outDir + '/' + makeName + '.so' funcName = library.function headers = library.getHeaders(self.platform) def takeFuncAddr(): # Try to include the necessary headers and get the function address. for header in headers: yield '#include %s' % header yield 'void (*f)() = reinterpret_cast(%s);' % funcName yield 'int main(int argc, char** argv) {' yield ' return 0;' yield '}' writeFile(sourcePath, takeFuncAddr()) try: compileOK = compileCommand.compile(self.log, sourcePath, objectPath) print >> self.log, '%s: %s header' % ( makeName, 'Found' if compileOK else 'Missing' ) if compileOK: linkOK = linkCommand.link(self.log, [ objectPath ], binaryPath) print >> self.log, '%s: %s lib' % ( makeName, 'Found' if linkOK else 'Missing' ) else: linkOK = False print >> self.log, ( '%s: Cannot test linking because compile failed' % makeName ) finally: remove(sourcePath) if isfile(objectPath): remove(objectPath) if isfile(binaryPath): remove(binaryPath) self.outVars['HAVE_%s_H' % makeName] = 'true' if compileOK else '' self.outVars['HAVE_%s_LIB' % makeName] = 'true' if linkOK else '' if linkOK: versionGet = library.getVersion( self.platform, self.configuration.linkStatic(), self.distroRoot ) if callable(versionGet): version = versionGet(compileCommand, self.log) else: version = resolve(self.log, versionGet) if version is None: version = 'error' self.outVars['VERSION_%s' % makeName] = version def iterProbeResults(probeVars, configuration, logPath): '''Present probe results, so user can decide whether to start the build, or to change system configuration and rerun "configure". ''' desiredComponents = set(configuration.iterDesiredComponents()) requiredComponents = set(configuration.iterRequiredComponents()) buildableComponents = set( comp for comp in desiredComponents if comp.canBuild(probeVars) ) packages = sorted( ( getPackage(makeName) for makeName in requiredLibrariesFor(desiredComponents) ), key = lambda package: package.niceName.lower() ) customVars = extractMakeVariables('build/custom.mk') yield '' if not parseBool(probeVars['COMPILER']): yield 'No working C++ compiler was found.' yield "Please install a C++ compiler, such as GCC's g++." yield 'If you have a C++ compiler installed and openMSX did not ' \ 'detect it, please set the environment variable CXX to the name ' \ 'of your C++ compiler.' yield 'After you have corrected the situation, rerun "configure".' yield '' else: # Compute how wide the first column should be. def iterNiceNames(): for package in packages: yield package.niceName for component in iterComponents(): yield component.niceName maxLen = max(len(niceName) for niceName in iterNiceNames()) formatStr = ' %-' + str(maxLen + 3) + 's %s' yield 'Found libraries:' for package in packages: makeName = package.getMakeName() if probeVars['HAVE_%s_LIB' % makeName]: found = 'version %s' % probeVars['VERSION_%s' % makeName] elif probeVars['HAVE_%s_H' % makeName]: # Dependency resolution of a typical distro will not allow # this situation. Most likely we got the link flags wrong. found = 'headers found, link test failed' else: found = 'no' yield formatStr % (package.niceName + ':', found) yield '' yield 'Components overview:' for component in iterComponents(): if component in desiredComponents: status = 'yes' if component in buildableComponents else 'no' else: status = 'disabled' yield formatStr % (component.niceName + ':', status) yield '' yield 'Customisable options:' yield formatStr % ('Install to', customVars['INSTALL_BASE']) yield ' (you can edit these in build/custom.mk)' yield '' if buildableComponents == desiredComponents: yield 'All required and optional components can be built.' else: if requiredComponents.issubset(buildableComponents): yield 'If you are satisfied with the probe results, ' \ 'run "make" to start the build.' yield 'Otherwise, install some libraries and headers ' \ 'and rerun "configure".' else: yield 'Please install missing libraries and headers ' \ 'and rerun "configure".' yield '' yield 'If the detected libraries differ from what you think ' \ 'is installed on this system, please check the log file: %s' \ % logPath yield '' def main(compileCommandStr, outDir, platform, linkMode, thirdPartyInstall): if not isdir(outDir): makedirs(outDir) logPath = outDir + '/probe.log' log = open(logPath, 'w') print 'Probing target system...' print >> log, 'Probing system:' try: distroRoot = thirdPartyInstall or None if distroRoot is None: if platform == 'darwin': for searchPath in environ.get('PATH', '').split(pathsep): if searchPath == '/opt/local/bin': print 'Using libraries from MacPorts.' distroRoot = '/opt/local' break elif searchPath == '/sw/bin': print 'Using libraries from Fink.' distroRoot = '/sw' break else: distroRoot = '/usr/local' elif platform.endswith('bsd') or platform == 'dragonfly': distroRoot = environ.get('LOCALBASE', '/usr/local') print 'Using libraries from ports directory %s.' % distroRoot elif platform == 'pandora': distroRoot = environ.get('LIBTOOL_SYSROOT_PATH') if distroRoot is not None: distroRoot += '/usr' print 'Using libraries from sysroot directory %s.' \ % distroRoot configuration = getConfiguration(linkMode) TargetSystem( log, logPath, compileCommandStr, outDir, platform, distroRoot, configuration ).everything() finally: log.close() if __name__ == '__main__': if len(sys.argv) == 6: try: main(*sys.argv[1 : ]) except ValueError, ve: print >> sys.stderr, ve sys.exit(2) else: print >> sys.stderr, ( 'Usage: python probe.py ' 'COMPILE OUTDIR OPENMSX_TARGET_OS LINK_MODE 3RDPARTY_INSTALL_DIR' ) sys.exit(2) openmsx-0.10.0/build/flavour-ppcg4.mk0000644000175000017500000000035612262345041020171 0ustar manuelmanuel00000000000000# Configuration for "ppc" flavour: # Optimised for PPC-G4 and higher. # Start with generic optimisation flags. include build/flavour-opt.mk # Add PPC specific flags. CXXFLAGS+=-mcpu=G4 -mtune=G4 -mpowerpc-gfxopt -maltivec -mabi=altivec openmsx-0.10.0/build/buildinfo2code.py0000644000175000017500000000733412262345041020414 0ustar manuelmanuel00000000000000from cpu import getCPU, X86, X86_64 from makeutils import extractMakeVariables, parseBool from outpututils import rewriteIfChanged import sys def iterBuildInfoHeader(targetPlatform, cpuName, flavour, installShareDir): platformVars = extractMakeVariables( 'build/platform-%s.mk' % targetPlatform, dict.fromkeys( ('COMPILE_FLAGS', 'LINK_FLAGS', 'TARGET_FLAGS', 'COMPILE_ENV', 'LINK_ENV', 'ANDROID_LDFLAGS', 'ANDROID_CXXFLAGS'), '' ) ) setWindowIcon = parseBool(platformVars.get('SET_WINDOW_ICON', 'true')) targetCPU = getCPU(cpuName) # TODO: Add support for device-specific configuration. platformDingux = targetPlatform == 'dingux' platformMaemo5 = targetPlatform == 'maemo5' platformPandora = targetPlatform == 'pandora' platformAndroid = targetPlatform == 'android' # Defaults. have16BPP = True have32BPP = True minScaleFactor = 1 maxScaleFactor = 4 # Platform overrides. if platformDingux: have32BPP = False maxScaleFactor = 1 elif platformAndroid: # At the moment, libsdl android crashes when trying to dynamically change the scale factor # TODO: debug why it crashes and then change the maxScaleFactor parameter here # so that people with a powerfull enough android device can use a higher scale factor have32BPP = False maxScaleFactor = 1 elif platformMaemo5: # TODO: These are in fact N900 specific settings, but we have no # support yet for device specific configuration and the N900 # is the most popular Maemo device currently. have32BPP = False maxScaleFactor = 2 elif platformPandora: have32BPP = False maxScaleFactor = 3 yield '// Automatically generated by build process.' yield '' yield '#ifndef BUILD_INFO_HH' yield '#define BUILD_INFO_HH' yield '' # Use a macro i.s.o. a boolean to prevent compilation errors on inline asm. # Assembly doesn't appear to work with MINGW64... TODO: find out why yield '#ifdef __MINGW64__' yield '#define ASM_X86 0' yield '#define ASM_X86 0' yield '#define ASM_X86_32 0' yield '#define ASM_X86_64 0' yield '#else' # A compiler will typically only understand the instruction set that it # generates code for. yield '#define ASM_X86 %d' % (targetCPU is X86 or targetCPU is X86_64) yield '#define ASM_X86_32 %d' % (targetCPU is X86) yield '#define ASM_X86_64 %d' % (targetCPU is X86_64) yield '#endif' # Use a macro iso integer because we really need to exclude code sections # based on this. yield '#define PLATFORM_DINGUX %d' % platformDingux yield '#define PLATFORM_MAEMO5 %d' % platformMaemo5 yield '#define PLATFORM_ANDROID %d' % platformAndroid yield '#define HAVE_16BPP %d' % have16BPP yield '#define HAVE_32BPP %d' % have32BPP yield '#define MIN_SCALE_FACTOR %d' % minScaleFactor yield '#define MAX_SCALE_FACTOR %d' % maxScaleFactor yield '' yield 'namespace openmsx {' yield '' # Note: Don't call it "BIG_ENDIAN", because some system header may #define # that. yield 'static const bool OPENMSX_BIGENDIAN = %s;' \ % str(targetCPU.bigEndian).lower() yield 'static const bool OPENMSX_UNALIGNED_MEMORY_ACCESS = %s;' \ % str(targetCPU.unalignedMemoryAccess).lower() yield 'static const bool OPENMSX_SET_WINDOW_ICON = %s;' \ % str(setWindowIcon).lower() yield 'static const char* const DATADIR = "%s";' % installShareDir yield 'static const char* const BUILD_FLAVOUR = "%s";' % flavour yield 'static const char* const TARGET_PLATFORM = "%s";' % targetPlatform yield '' yield '} // namespace openmsx' yield '' yield '#endif // BUILD_INFO_HH' if __name__ == '__main__': if len(sys.argv) == 6: rewriteIfChanged(sys.argv[1], iterBuildInfoHeader(*sys.argv[2 : ])) else: print >> sys.stderr, \ 'Usage: python buildinfo2code.py CONFIG_HEADER ' \ 'platform cpu flavour share-install-dir' sys.exit(2) openmsx-0.10.0/build/platform-nacl.mk0000644000175000017500000000057212262345041020237 0ustar manuelmanuel00000000000000# Configuration for Chrome Native Client (NaCl). # Pick the right compiler. ifeq ($(OPENMSX_TARGET_CPU),x86) NACL_CPU:=i686 else NACL_CPU:=$(OPENMSX_TARGET_CPU) endif CXX:=$(NACL_CPU)-nacl-g++ # Does platform support symlinks? USE_SYMLINK:=false # File name extension of executables. EXEEXT:=.nexe LIBRARYEXT:=.so # Build a minimal set of components. LINK_MODE:=3RD_STA_MIN openmsx-0.10.0/build/3rdparty.mk0000644000175000017500000002433212262345041017250 0ustar manuelmanuel00000000000000# Compiles 3rd party libraries needed by openMSX. # It enables only the features needed by openMSX. ifeq ($(origin PYTHON),undefined) $(error You should pass PYTHON) endif ifeq ($(origin BUILD_PATH),undefined) $(error You should pass BUILD_PATH) endif ifeq ($(origin OPENMSX_TARGET_OS),undefined) $(error You should pass OPENMSX_TARGET_OS) endif ifeq ($(origin OPENMSX_TARGET_CPU),undefined) $(error You should pass OPENMSX_TARGET_CPU) endif .PHONY: all all: default # Get information about packages. -include derived/3rdparty/packages.mk ifneq ($(origin PACKAGE_SDL),undefined) # These libraries are part of the base system, therefore we do not need to # link them statically for building a redistributable binary. SYSTEM_LIBS:=$(shell $(PYTHON) build/list_system_libs.py $(OPENMSX_TARGET_OS)) # Compiler selection, compiler flags, SDK selection. # These variables are already exported, but we make it explicit here. export CC export LD export NEXT_ROOT export MACOSX_DEPLOYMENT_TARGET CC=$(_CC) LD=$(_LD) TIMESTAMP_DIR:=$(BUILD_PATH)/timestamps BUILD_DIR:=$(BUILD_PATH)/build INSTALL_DIR:=$(BUILD_PATH)/install # Create a GNU-style system triple. ifeq ($(OPENMSX_TARGET_CPU),x86) TRIPLE_MACHINE:=i686 else ifeq ($(OPENMSX_TARGET_CPU),ppc) TRIPLE_MACHINE:=powerpc else ifeq ($(OPENMSX_TARGET_CPU),ppc64) TRIPLE_MACHINE:=powerpc64 else TRIPLE_MACHINE:=$(OPENMSX_TARGET_CPU) endif endif endif ifeq ($(OPENMSX_TARGET_OS),dingux) TRIPLE_OS:=linux else TRIPLE_OS:=$(OPENMSX_TARGET_OS) endif TARGET_TRIPLE:=$(TRIPLE_MACHINE)-unknown-$(TRIPLE_OS) # Work around some autoconf versions returning "universal" for endianess when # compiling with "-arch" in the CFLAGS, even in a single arch compile. BIGENDIAN:=$(shell PYTHONPATH=build/ $(PYTHON) -c 'from cpu import getCPU ; print "yes" if getCPU("$(OPENMSX_TARGET_CPU)").bigEndian else "no"') ifeq ($(BIGENDIAN),) $(error Could not determine endianess of "$(OPENMSX_TARGET_CPU)") endif # Although X11 is available on Windows and Mac OS X, most people do not have # it installed, so do not link against it. ifeq ($(filter linux freebsd netbsd openbsd gnu,$(OPENMSX_TARGET_OS)),) USE_VIDEO_X11:=disable else USE_VIDEO_X11:=enable endif PACKAGES_BUILD:=$(shell $(PYTHON) build/3rdparty_libraries.py $(OPENMSX_TARGET_OS) $(LINK_MODE)) PACKAGES_NOBUILD:= ifeq ($(OPENMSX_TARGET_OS),mingw32) PACKAGES_NOBUILD+=DIRECTX endif PACKAGES_3RD:=$(PACKAGES_BUILD) $(PACKAGES_NOBUILD) BUILD_TARGETS:=$(foreach PACKAGE,$(PACKAGES_BUILD),$(TIMESTAMP_DIR)/build-$(PACKAGE_$(PACKAGE))) INSTALL_BUILD_TARGETS:=$(foreach PACKAGE,$(PACKAGES_BUILD),$(TIMESTAMP_DIR)/install-$(PACKAGE_$(PACKAGE))) INSTALL_NOBUILD_TARGETS:=$(foreach PACKAGE,$(PACKAGES_NOBUILD),$(TIMESTAMP_DIR)/install-$(PACKAGE_$(PACKAGE))) ifeq ($(filter $(PACKAGES_3RD),DIRECTX),) INSTALL_DIRECTX:= else INSTALL_DIRECTX:=$(TIMESTAMP_DIR)/install-$(PACKAGE_DIRECTX) endif INSTALL_PARAMS_GLEW:=\ GLEW_DEST=$(PWD)/$(INSTALL_DIR) \ LIBDIR=$(PWD)/$(INSTALL_DIR)/lib # Function which, given a variable name prefix and the variable's value, # returns the name of the package. findpackage=$(strip $(foreach PACKAGE,$(PACKAGES_3RD),$(if $(filter $(2),$($(1)_$(PACKAGE))),$(PACKAGE),))) .PHONY: default default: $(INSTALL_BUILD_TARGETS) $(INSTALL_NOBUILD_TARGETS) .PHONY: clean clean: rm -rf $(SOURCE_DIR) rm -rf $(BUILD_DIR) rm -rf $(INSTALL_DIR) # Install. $(INSTALL_BUILD_TARGETS): $(TIMESTAMP_DIR)/install-%: $(TIMESTAMP_DIR)/build-% $(MAKE) -C $(BUILD_DIR)/$* install $(INSTALL_PARAMS_$(call findpackage,PACKAGE,$*)) mkdir -p $(@D) touch $@ ifneq ($(INSTALL_DIRECTX),) # Install DirectX headers. $(INSTALL_DIRECTX): $(TARBALL_DIRECTX) mkdir -p $(INSTALL_DIR) tar -zxf $< -C $(INSTALL_DIR) mkdir -p $(@D) touch $@ endif # Build. $(BUILD_TARGETS): $(TIMESTAMP_DIR)/build-%: $(BUILD_DIR)/%/Makefile $(MAKE) -C $( $@ openmsx-0.10.0/build/version2code.py0000644000175000017500000000125212262345041020117 0ustar manuelmanuel00000000000000# Generates version include file. from outpututils import rewriteIfChanged from version import extractRevisionString, packageVersion, releaseFlag import sys def iterVersionInclude(): revision = extractRevisionString() yield '// Automatically generated by build process.' yield 'const bool Version::RELEASE = %s;' % str(releaseFlag).lower() yield 'const char* const Version::VERSION = "%s";' % packageVersion yield 'const char* const Version::REVISION = "%s";' % revision if __name__ == '__main__': if len(sys.argv) == 2: rewriteIfChanged(sys.argv[1], iterVersionInclude()) else: print >> sys.stderr, \ 'Usage: python version2code.py VERSION_HEADER' sys.exit(2) openmsx-0.10.0/build/download.py0000644000175000017500000000451412262345041017330 0ustar manuelmanuel00000000000000from os import remove, stat from os.path import basename, isdir, isfile, join as joinpath from urllib import FancyURLopener from urlparse import urlparse import sys # FancyURLOpener, which is also used in urlretrieve(), does not raise # an exception on status codes like 404 and 500. However, for downloading it is # critical to write either what we requested or nothing at all. class DownloadURLOpener(FancyURLopener): def http_error_default(self, url, fp, errcode, errmsg, headers): raise IOError('%s: http:%s' % (errmsg, url)) _urlOpener = DownloadURLOpener() class StatusLine(object): def __init__(self, out): self._out = out def __call__(self, message, progress = False): raise NotImplementedError class InteractiveStatusLine(StatusLine): __length = 0 def __call__(self, message, progress = False): self._out.write(('\r%-' + str(self.__length) + 's') % message) self.__length = max(self.__length, len(message)) class NoninteractiveStatusLine(StatusLine): def __call__(self, message, progress = False): if not progress: self._out.write(message + '\n') def createStatusLine(out): if out.isatty(): return InteractiveStatusLine(out) else: return NoninteractiveStatusLine(out) def downloadURL(url, localDir): if not isdir(localDir): raise IOError('Local directory "%s" does not exist' % localDir) fileName = basename(urlparse(url).path) localPath = joinpath(localDir, fileName) prefix = 'Downloading %s: ' % fileName statusLine = createStatusLine(sys.stdout) statusLine(prefix + 'contacting server...') def reportProgress(blocksDone, blockSize, totalSize): doneSize = blocksDone * blockSize statusLine(prefix + ( '%d/%d bytes (%1.1f%%)...' % ( doneSize, totalSize, (100.0 * doneSize) / totalSize ) if totalSize > 0 else '%d bytes...' % doneSize ), True) try: try: _urlOpener.retrieve(url, localPath, reportProgress) except IOError: statusLine(prefix + 'FAILED.') raise else: statusLine(prefix + 'done.') finally: print except: if isfile(localPath): statusLine(prefix + 'removing partial download.') remove(localPath) raise if __name__ == '__main__': if len(sys.argv) == 3: try: downloadURL(*sys.argv[1 : ]) except IOError, ex: print >> sys.stderr, ex sys.exit(1) else: print >> sys.stderr, \ 'Usage: python download.py url localdir' sys.exit(2) openmsx-0.10.0/build/platform-netbsd.mk0000644000175000017500000000027512262345041020601 0ustar manuelmanuel00000000000000# Configuration for NetBSD. # Does platform support symlinks? USE_SYMLINK:=true # File name extension of executables. EXEEXT:= LIBRARYEXT:=.so COMPILE_FLAGS+=-D_REENTRANT -D_THREAD_SAFE openmsx-0.10.0/build/win_resource.py0000644000175000017500000000144012262345041020220 0ustar manuelmanuel00000000000000# Generates Windows resource header. from outpututils import rewriteIfChanged from version import extractRevisionNumber, packageVersion import sys def iterResourceHeader(): if '-' in packageVersion: versionNumber = packageVersion[ : packageVersion.index('-')] else: versionNumber = packageVersion revision = str(extractRevisionNumber()) versionComponents = versionNumber.split('.') + [ revision ] assert len(versionComponents) == 4, versionComponents yield '#define OPENMSX_VERSION_INT %s' % ', '.join(versionComponents) yield '#define OPENMSX_VERSION_STR "%s\\0"' % packageVersion if __name__ == '__main__': if len(sys.argv) == 2: rewriteIfChanged(sys.argv[1], iterResourceHeader()) else: print >> sys.stderr, \ 'Usage: python win-resource.py RESOURCE_HEADER' sys.exit(2) openmsx-0.10.0/build/components.py0000644000175000017500000000227412262345041017707 0ustar manuelmanuel00000000000000# Defines the building blocks of openMSX and their dependencies. class Component(object): niceName = None makeName = None dependsOn = None @classmethod def canBuild(cls, probeVars): return all( probeVars.get('HAVE_%s_H' % makeName) and probeVars.get('HAVE_%s_LIB' % makeName) for makeName in cls.dependsOn ) class EmulationCore(Component): niceName = 'Emulation core' makeName = 'CORE' dependsOn = ('SDL', 'SDL_TTF', 'PNG', 'TCL', 'XML', 'ZLIB') class GLRenderer(Component): niceName = 'GL renderer' makeName = 'GL' dependsOn = ('GL', 'GLEW') class Laserdisc(Component): niceName = 'Laserdisc' makeName = 'LASERDISC' dependsOn = ('OGG', 'VORBIS', 'THEORA') class AODriver(Component): niceName = 'Libao sound driver' makeName = 'AO' dependsOn = ('AO', ) def iterComponents(): yield EmulationCore yield GLRenderer yield Laserdisc yield AODriver def requiredLibrariesFor(components): '''Compute the library packages required to build the given components. Only the direct dependencies from openMSX are included, not dependencies between libraries. Returns a set of Make names. ''' return set( makeName for comp in components for makeName in comp.dependsOn ) openmsx-0.10.0/build/flavour-devel.mk0000644000175000017500000000047112262345041020251 0ustar manuelmanuel00000000000000# Configuration for "devel" flavour: # Build with debug symbols, no debug prints, some optimisations. # Debug flags. CXXFLAGS+=-O2 -g # Extra warnings, only works with recent gcc versions #CXXFLAGS+=-ansi -pedantic -Wno-long-long -Wextra -Wno-missing-field-initializers # Strip executable? OPENMSX_STRIP:=false openmsx-0.10.0/build/flavour-ppc.mk0000644000175000017500000000027212262345041017733 0ustar manuelmanuel00000000000000# Configuration for "ppc" flavour: # Optimised for G3 PPC. # Start with generic optimisation flags. include build/flavour-opt.mk # Add PPC specific flags. CXXFLAGS+=-mcpu=G3 -mtune=G4 openmsx-0.10.0/build/3rdparty_libraries.py0000644000175000017500000000173012262345041021322 0ustar manuelmanuel00000000000000# Prints which 3rd party libraries are desired for the given configuration. from components import requiredLibrariesFor from configurations import getConfiguration from libraries import allDependencies, librariesByName from packages import iterDownloadablePackages def main(platform, linkMode): configuration = getConfiguration(linkMode) components = configuration.iterDesiredComponents() # Compute the set of all directly and indirectly required libraries, # then filter out system libraries. thirdPartyLibs = set( makeName for makeName in allDependencies(requiredLibrariesFor(components)) if not librariesByName[makeName].isSystemLibrary(platform) ) print ' '.join(sorted(thirdPartyLibs)) if __name__ == '__main__': import sys if len(sys.argv) == 3: try: main(*sys.argv[1 : ]) except ValueError, ex: print >> sys.stderr, ex sys.exit(2) else: print >> sys.stderr, ( 'Usage: python 3rdparty_libraries.py TARGET_OS LINK_MODE' ) sys.exit(2) openmsx-0.10.0/Contrib/0000755000175000017500000000000012262345041015444 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/README.cbios0000644000175000017500000002224012262345041017422 0ustar manuelmanuel00000000000000C-BIOS 0.25 =========== This software is a substitute BIOS which is can be used for running MSX emulators. It currently supports only execution of cartridge image ("ROMs"). Before you use it, you should read and accept the license (see below). On the C-BIOS web site, you can download newer versions, download the source code, and report bugs. http://cbios.sourceforge.net/ License ------- Copyright (c) 2002-2005 BouKiCHi. All rights reserved. Copyright (c) 2003 Reikan. All rights reserved. Copyright (c) 2004-2006,2008-2010 Maarten ter Huurne. All rights reserved. Copyright (c) 2004-2006,2008-2011 Albert Beevendorp. All rights reserved. Copyright (c) 2004-2005 Patrick van Arkel. All rights reserved. Copyright (c) 2004,2010-2011 Manuel Bilderbeek. All rights reserved. Copyright (c) 2004-2006 Joost Yervante Damad. All rights reserved. Copyright (c) 2004-2006 Jussi Pitkänen. All rights reserved. Copyright (c) 2004-2007 Eric Boon. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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 AUTHOR ``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 AUTHOR 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. History ------- ver 0.01 Initial ver 0.02 2002-08-15(JST) * Added original font and drawing screen. * Added dump-mode. * Changed recognition method of cartridges to recognize cartridges taking priority. ver 0.03 2002-08-19(JST) * Based on a suggestion from Ms.Okei, wrote 20h of BIOS(compare HL and DE). In the result, shooting game of a certain company became runnable more correctly. Thank Ms.Okei!! ver 0.04 2002-08-20(JST) * Added initialize of FCC2h-FCC4h. * Added function of GTSTCK and GTTRIG temporarily. * Divided msxb.bin to halfs. doing combining/copying with setb.bat now. ver 0.05 2002-08-27(JST) * Added INITGRP(only screen2), CHGMOD(graphic mode change routine), a routine calls H.STKE. * Rewrite memory recognition routine. * Some bug fixes. * Added sound test function. ver 0.06 2002-09-01(JST) * Fixed around of color. ver 0.07 2002-09-09(JST) * Added some sorts of keyboard routines. * Added joystich function to GTSTCK and GTTRIG. ver 0.08 2002-09-12(JST) * Restructured memory initialize routine. * Added error display routine. * Fixed routine of finding kinds of cartridges. * Fixed using method of EXPTBL. * Added initialize of from RG8SAV to RG23SA. * Now return within disabled interrupt from ENASLT routine. ver 0.09 2002-09-19(JST) * Made the rest half of font. * Improved key input routine. * Added CHPUT. With it, rewrote display routine. * Fixed init_grp. * Changed filenames to CBIOS.ROM, CBIOS_SUB.ROM. ver 0.10 2002-09-20(JST) * Fixed indent. * and so on... ver 0.10a 2002-09-22(JST) * Fixed license. * Added support of ROMs in page3. ver 0.11 2002-09-22(JST) * Small fix in init_sc5. ver 0.12beta 2002-09-25(JST) * Added test routine for disk access. need DISK.ROM. * Added init_sc7. * Improved ENASLT. now finding cartridge uses ENASLT. * Improved RAM detection. ver 0.12 2002-09-27(JST) * Changed finding cartridge again. * Changed screen mode of cartridge running time. * Fixed keyboard routine. * Fixed stick routine against to interrupt. ver 0.13 2002-10-02(JST) * Based on info from Mr.Maarten (a member of openMSX developers), fixed around of SCREEN 5. For detail, switching line numbers, temporary treatment for a bug of reading from VDP status register, and so on. ver 0.14 2002-10-10(JST) * Rewrote comments in source within Japanese. ver 0.15 2003-02-26(JST) * Rewrote some of comments back to English again. * Fixed non-assemblable condition becauseof lack of font file. * Changed filename, some of label name, strings and so on. ver 0.16 2003-04-16(JST) * Separated sound test from source. (Disabled) ver 0.16a 2003-06-01(JST) * CHGMOD: When screen0/1, now load font to VRAM. * CHPUT: Now support also screen1 not only screen0. ver 0.16b 2003-08-10(JST) * Added entry: INITXT, INIT32. These were exist only as internal routine of CHGMOD. * INITXT, INIT32: Fixed screen clear failure. * CHPUT: Fixed scroll failure. ver 0.17 2003-08-10(JST) * Changed LICENSE. New LICENSE will be suitable in various situations. e.g. use as a firmware for hand-made hardware. ver 0.18 2004-12-18(CET) * First release since moving to SourceForge. * Much improved support for MSX2 games. * Graphical boot logo. * Included machine config files for several MSX emulators. * Various bug fixes. ver 0.19 2004-12-24(CET) * Added support for SCREEN4 and SCREEN8. * Added support for clock chip. * Added support for palette. This fixes a lot of wrong colours. * Stubbed many calls: non-implemented calls print their name on the openMSX debugdevice (if present). * Various bug fixes. ver 0.20 2005-02-09(CET) * Added an MSX2+ configuration, which includes V9958 and MSX MUSIC. * Separate main ROMs for MSX1/MSX2/MSX2+. * Implemented several MSX2 specific routines, including BLT*. * Display is disabled when switching to a different screen mode. * Improved CHPUT a lot; implemented control and escape codes. * Rewrote key buffering; fixes bug of keys being repeated. * New boot logo, even cooler than the previous one. * New font, placed at a fixed address so all games can find it. * Started work on a disk ROM, but it is not functional yet, so it is not enabled in the configurations. * Stubbed all non-implemented calls. * Various bug fixes. ver 0.21 2005-06-07(CET) * Fixed RuMSX configuration files, thanks to Rudolf Lechleitner. * Rewrote ROM search code; now all ROMs are recognized. Also a clear error message is printed for BASIC ROMs. * New boot logo for MSX2 and MSX2+. * Changed boot sequence: Show logo, switch to SCREEN 1 and search for ROMs. * Improved video code; fixes several games. * Various bug fixes. ver 0.22 2008-12-27(CET) * Use separate logo ROM to save space in the main ROM. * Set lower bits of PSG reg 15 before reading joystick trigger status. * Improved RAM search. * Many new routines implemented and existing implementations made more complete, especially character I/O and bitmap graphics. * Added lots of documentation to system variables. * Added support for GNU assembler. * Various bug fixes. ver 0.23 2009-01-04(CET) * Updated blueMSX configuration files, thanks to Benoît Delvaux. * Fixed version reported by MSX1 logo ROM. * Fixed several video routines so they work on MSX1 VDPs (TMS99xx). * A couple of other bug fixes. ver 0.24 2010-05-24(CET) * VRAM size is now properly checked, fixing R-Type's V9938 detection. * C-BIOS doesn't lie anymore about the interrupt frequency. * Don't di; halt when no ROM is found, the warning in openMSX may be confusing * A few minor bug fixes and tweaks. ver 0.25 2011-02-01(CET) * C-BIOS now offers localized versions in the flavours INT (default), JP and BR. * Bug fixes for compatibility with Mirai, Family Billiards. * A couple of other bug fixes. * This version only compiles with Pasmo 0.5.3, due to lack of standards in assembler directives... Special Thanks -------------- People uploading MSX information to the internet. People developing any kind of emulators. All users. Font edit tool: Gameboy Tile Designer version 2.2 Copyright H. Mulder 1999 openmsx-0.10.0/Contrib/basictorom.tcl0000644000175000017500000000414112262345041020312 0ustar manuelmanuel00000000000000### basictorom.tcl ### # # This script was developed together with Daniel Vik to have an automated tool # to convert BASIC programs to ROM files. See this forum thread for more # details: # # http://www.msx.org/forumtopic9249.html # # To use it, put a file 'prog.bas' in the current directory (can be either in # ascii format or an already tokenized basic file). Then execute this script by # using the openMSX commandline. And after a few seconds the ROM image # 'prog.rom' will be generated. # # input: prog.bas # output prog.rom # start with: openmsx -script basictorom.tcl # # Note: This script only works on MSX machines that have a disk drive and have # MSX-BASIC built-in. So for example it won't work on the default C-bios # based machines. So either select a different MSX machine as your # default machine, or pass the '-machine ' as extra # option when starting openMSX. # proc do_stuff1 {} { # insert openMSX ramdisk in the MSX disk drive diska ramdsk # import host file to ramdisk diskmanipulator import diska "prog.bas" # change basic start address poke16 0xf676 0x8011 # add rom header poke 0x8000 0x41 poke 0x8001 0x42 poke16 0x8002 0x0000 poke16 0x8004 0x0000 poke16 0x8006 0x0000 poke16 0x8008 0x8010 poke16 0x800a 0x0000 poke16 0x800c 0x0000 poke16 0x800e 0x0000 poke 0x8010 0x00 # instruct MSX to load the BASIC program type "load\"prog.bas\"\r" # give MSX some time to process this # wait long enough so that even very long BASIC programs can be loaded after time 100 do_stuff2 } proc do_stuff2 {} { # save rom file set data [debug read_block "memory" 0x8000 0x4000] set file [open "prog.rom" "WRONLY CREAT TRUNC"] fconfigure $file -translation binary puts -nonewline $file $data close $file # exit emulator exit } # don't store the settings below for future openmsx sessions set save_settings_on_exit false # don't show MSX screen (remove if you want to see what's going on) set renderer none # go as fast as possible set throttle off # give emulated MSX some time to boot after time 20 do_stuff1 openmsx-0.10.0/Contrib/node.mk0000644000175000017500000000030112262345041016714 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ README \ README.cbios cbios \ README.openmsx-control \ openmsx-control-stdio.cc openmsx-control-socket.cc \ basictorom.tcl include build/node-end.mk openmsx-0.10.0/Contrib/README0000644000175000017500000000064212262345041016326 0ustar manuelmanuel00000000000000About Contrib ============= This directory contains third-party software that is distributed together with openMSX. These packages are not part of openMSX: they are maintained and licensed separately. Please read the README. file and the documentation files of the contributed packages for details. We would like to thank the contributors for the software they created and for allowing us to distribute it. openmsx-0.10.0/Contrib/cbios/0000755000175000017500000000000012262345041016543 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_BR/0000755000175000017500000000000012262345041020726 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_BR/roms/0000755000175000017500000000000012262345041021706 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_BR/roms/cbios_sub.rom0000644000175000017500000004000012262345041024367 0ustar manuelmanuel00000000000000CD~·ÉÉÉÉ|ºÀ}»ÉÉÉÉÉÉÉÉÉÉõÅÕåÙõÅÕåÝåýåÝ!8ý*ÀüÍÅýáÝááÑÁñÙáÑÁñíMÉÉÉÉÉÍÖýíEÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃòûÃûÃü ûÃsûÃyûÃgûÊûÃ…û× û×ûé ûÃê ûÃÙ ûÃÉ ûú ûÃÐûÃáûéûà ûóûÃÅûÃûÃ`ûÃ¥ûÃøûÃPûÃûòûÃfûÃûÃûÃ%ûÃu ûÃÊ ûÃÄ ûÃûÃn ûÃMÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃÿûÃÒ ÉÉÉÉÉÉÉÉûÃaûüûÃØûÃïûÃÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃ|ûÃŒûÞûðÉÉÉÉûÃûÃûÃ’ûÃòûÃûÃûÃ%ûÃ6ÉÉÉÉûÃHÉÉÉÉûÃOÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃaûÃ~ÉÉÉO þ#(##øÉN#fié>#Ó.Í">Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ*ñÍ*É|Í:}Í:ÉÅåõW§óüoñáåÕåõ|æoG>ü͉_Eñæ͉GÛ¨W£°áÍ€ó{ÑõËzÄ«ñáÁÉÈüÉåWÕ§óüoÁáÕå|æoG>ü͉_Ezæ͉GÛ¨W£°áYÍ…óÑåËzÄ«áÉÙóýåñÝåáW§üoÕzæGüÝåñæ‡(ËË= ù!øåÛ¨õ¡°ÙÃŒóÙóÑËzÄ«ÙÉóåoæG>«ÆUò W|ægG>Àò_/Oz£G}§ògæåÅG>«ÆUò6£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝé:àóæ¿GÍÿÉ:àóö@GÍÿÉó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ0Û˜ÉõÍBñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍB͈ xAO ñÓ˜ü ùÉ:¯üþ0Í0Í~ åë xA<˜í²= ûáÉë:¯üþ0ÍB͈ ë xA<˜í³= ûëÉþ Ð!¼ÃÅ`¥s ý [ ·  !èÿ:¯üþ0˾Ëþ¯2ìÿ¯2öÿó!ßó™€í£xíQ· ÷>Ó™>‘Ó™!çÿ›í³###>Ó™>‘Ó™›í³ûÃó:¯üþ(:=õ:éóæðo:ëóµGÍÿñÀ:éóæð!êó¶*¿ó õÍBñÓ˜õ x± ÷ñÉ:ëóGÃÿ:¯ü·ÈÍ:¯üþ8*(ù%%:éóæÍi *&ù¯Íi É:¯üþ8ÙÑ*(ù͈ :éóW ó{Ó˜>Ó˜yÓ˜ Í%0 zÓ˜èûÉÍç>2¯ü2°ü¯2õú2öú:®ó2°ó>2Üó2Ýó*³ó""ù*·ó:°óþ)8!"$ù*¹ó"(ù*»ó"&ùÍÍøÍ\ Í ÃÎÍç>2¯ü2°ü>2Üó2ÝóÍ*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍ\ :¯ó2°ó¯2õú2öúÍP͛͞ ÃÎÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù¯2õú2öúÍÍ›ͽ ÃÎÍç>2¯üÍ*Ñó""ùÍB¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ù¯2õú2öúͲÍ›ÍÓ ÃÎ:°óþ)0(:ßóæñGÍÿ:àóæçöG Íÿ³ó¯Íã ¯ÍãÉ:ßóæñöGÍÿ:àóæçöG Íÿ"ù>Íã ¯ÍãÉ:ßóæñGÍÿ:àóæçG Íÿ½ó¯Íã¯Íã¯Íã¯Íã¯ÍãÉ:ßóæñöGÍÿ:àóæçG ÍÿÇó¯Íã>Íã>Íã¯Íã¯ÍãÉ:ßóæñGÍÿ:àóæçöG ÍÿÑó¯Íã¯Íã¯Íã¯Íã¯ÍãÉÕõ! Fë~#fo)üGñ°GÍÿÑ É  &o)))Í%0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍu í[¹üíK·üÍ :éó2òó**ùíKËó @ü:¹üæO Í«ð :¹ü/æOÍ«*·ü "·üñÁÑáÉ:·üæõÅÕåÍ4 :·üæ(:,ù/2,ùá ÑÁñÍ4 :,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍ"õæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í(Ýñæð°Í(ÚAŽÓ™á}Ó™ÉÍ~ Û˜Éõ͈ ñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™ÍÿàÍÿÍÿ€ÍÿÍÿÍÿ>2Üó2ÝóÍž >!ÍV>õ!  ÍVõÍÿ!¿ý*ÀüÝ!\ÍÅûÉÍÿ;ÍÿÍÿÉ!¿í[$ùý*ÀüÝ!\ÍÅûÉÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù:ßóæñöGÍÿ:àóæçG ÍÿÍÿÇó¯Íã>Íã>Íãû >ÍãÏó¯Íã!éÿ6#6Í›Íÿͽ ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçö G Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Íç ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçG Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Í÷ ÃÎÍç>2¯üÍ:ßóæñö GÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÍç>2¯üÍ:ßóöGÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÀ:¯üþ Ðå!} ÍáÉ ž ½ Ó ½ ç ÷  :°óþ(À8€*"ù> ÍV!"Üó}!²ûw³ûí°É¯*$ùoÅÍVÁ:êó*ÉóÃV:êóæG°*$ùÃV:êóæG°!):êóæG°°!:êóæG°!:êó!õÍl ñ,Íb (Í\ !Ô*Í\ !$Í\ :öúg.&Í\ >-Íb >À.Íb Íl É}Íb |óÓ™yö€Ó™ûÉ>ÍÒ 8øÉíK ùo&))) @üÅÕå:ùÍPáÑÁ#ïÉåõ!¢ ÍñáÉRIGHTCåõ!´ ÍñáÉLEFTCåõ!Å ÍñáÉUPCåõ!Ô ÍñáÉTUPCåõ!ä ÍñáÉDOWNCåõ!õ ÍñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!`ÍñáÉSTORECåõ!rÍñáÉSETATRåõ!„ÍñáÉREADCåõ!•ÍñáÉSETCåõ!¥ÍñáÉNSETCXåõ!·ÍñáÉGTASPCåõ!ÉÍñáÉPNTINIåõ!ÛÍñáÉSCANRåõ!ìÍñáÉSCANLåõ!ýÍñáÉDOGRPHÅÕåÍu :éó2òó*·ü"fõ "·ü:öú*¹üg"hõíCjõíClõ!@üË:òó8:êó2nõÍÂ:ûæö°íy>¬Ó™>‘Ó™û!@üË:òó8:êóÓ› ñ#ìáÑÁÉåõ!~ÍñáÉMAPXYCåõ!ÍñáÉTRIGHTåõ!¢ÍñáÉTRIGHT:öúWÕíCfõíShõ*³ü§íB"jõ!"lõ¯2oõ:òó2nõÍý*µü:öúg"hõÍýÑíShõ>2oõ:µü“2jõÍý*³ü"fõÍýÉÍÂ:ûæöpíyûÉ*µüç0ë§íR#"lõ:öúWíShõÅÑ*³üç0ë§íR#"jõíSfõ¯2oõ:òó2nõÍÂ:ûæö€íyûÉåõ!ZÍñáÃn CLRTXTåÕÅ*"ùËË:õú„öGÍÿ:¯üþ :åóæG:õú°GÍÿ:õúO:¯üþ Ë!¯*(ù)±o>´GÍÿE ÍÿÁÑáÉåÍ'͈ Íÿ !\~Ó˜#ÓšøáÉåÍ'Í~ áÍÿ Û˜ÓšúÉåõÍ'ñ‡O Í~ Û˜GÛ˜OáÉõÅåÍ'z‡O ͈ áBÍÿÁñÓšÓ˜{ÓšÓ˜É:¯ü·Ì>ÍÒ 8øó> Ó™>‘Ó™›!bõí³Éx±7Èz³7Èå!:¯üæþ:¯ü $·íBáØå!Ô·íRáÉíKjõí[lõÍÜØÍÂ~æöíyû·É*bõN#F#íCjõ^#V#íSlõÍÜØþ( þ(N¯CËËø2nõBÅåÍÂ~æö°íy>¬Ó™>‘Ó™ûáÁ>ÍÒ ËGÈË(ôx§(ůCËËøÓ›yÁOî#NBÙ*fõíKjõí[lõq#p#s#r#ÍÜØþ( þ(åÍÂ~æ> íyûáB>ÍÒ Ë ËG óÅ>ÍÒ CËü±ÁOãq#>ÍÒ 8ÖÉåõ!ýÍñáÉBLTVDåõ!ÍñáÉBLTDVåõ!ÍñáÉBLTMDåõ!0ÍñáÉBLTDMåõ!AÍñáÉNEWPADͳͼÉåõ!ZÍñáÉKNJPRTÅ> Ó´yæGÛµ°ÁÓµyæÓ´ÛµæÉÅGÅ> Ó´yæGÛµ°ÁÓµyæÓ´xæÓµÁÉopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_BR/roms/cbios_music.rom0000644000175000017500000004000012262345041024716 0ustar manuelmanuel00000000000000ABAPRLOPLLÃ%AÃMAÃuAÃAÃÄAÃìAÃBåõ!0AÍ#Ó.ÍHB>Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍPBñÍPBÉ|Í`B}Í`BÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_BR/roms/cbios_main_msx2+_br.rom0000644000175000017500000010000012262345041026226 0ustar manuelmanuel00000000000000óà ¿˜˜Ã’ÃÒ#äÃ$ÃÀÃG$ÃÆÆ$ÃÌ!ÃÞÚÃóÃýÃÃ"Ã.ÃZÃ`ÃhÃzÃŽíÃÌÃëÃôÃÃ>ÃæïøÃÁÃÃHÃ{ÃÑÃäÃîÃùÃîÃÃÃÃ!Ã4ÃYÃ÷ÃÅÃêÃÃÿÃÙÃõÃà Ã2ÃÃÃ/ÃAÃSÃCöÃûà ÃfÃyËÃðÃÃÃÕÃÃ-ÃYÃkÃ|ËÛìþÃÎà à Ã) Ã; ÃL Ã\ Ãn À Ã’ ã Ã[ÃÃÃ!Ã$Ã'Ã6Ã:Ã>ÃgÃyÃ}ÓÃ&ÃïÃõÃÞÃ5Ã=Ã;ÃPÃZÖÜÃNÃRO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóyþ8!ßÿþ8 ( þ0!áÿx wáÉÍhÛ˜ÉõÍzñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍzÍZ xAO ñÓ˜ü ùÉ:¯üþ0ÍhÍPåë xA<˜í²= ûáÉë:¯üþ0ÍzÍZë xA<˜í³= ûëÉÝåÝ!ÑÃï:¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍzñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍh:¯üþ8*(ù%%:éóæÍ;*&ù¯Í;É:¯üþ8ÙÑ*(ùÍZ:éóW ó{Ó˜>Ó˜yÓ˜ Íî0 zÓ˜èûÉÝåÝ!ÕÃïÝåÝ!ÙÃïÝåÝ!ÝÃïÝåÝ!áÃï:°óþ)0(:ßóæñGÍ.:àóæçöG Í.³ó¯Í¬ ¯Í¬É:ßóæñöGÍ.:àóæçöG Í."ù>ͬ ¯Í¬É:ßóæñGÍ.:àóæçG Í.½ó¯Í¬¯Í¬¯Í¬¯Í¬¯Í¬É:ßóæñöGÍ.:àóæçG Í.Çó¯Í¬>ͬ>ͬ¯Í¬¯Í¬É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í¬¯Í¬¯Í¬¯Í¬¯Í¬ÉÕõ!Ê Fë~#fo)üGñ°GÍ.Ñ É  &o)))Íî0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ(+þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÝåÝ!‰ÃïÉñåÕÅõÍ7í[¹üíK·üÍÎ:éó2òó**ùíKËó @ü:¹üæO Í}ð :¹ü/æOÍ}*·ü "·üñÁÑáÉ:·üæõÅÕåÍ:·üæ(:,ù/2,ùá ÑÁñÍ:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍZõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í`Ýñæð°Í`ÚAŽÓ™á}Ó™ÉÍPÛ˜ÉõÍZñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍc>!ÍŽ>õ!  ÍŽõÍ.!¿ÍÌÉÍ.;Í.Í.É!¿í[$ùÃÌÀ:¯üþ Ðå!BÍáÉTc•©¹ÌÜ:°óþ(À8€*"ù> ÍŽ>!²ûw³ûí°Ãê¯*$ùoÅÍŽÁ:êó*ÉóÃŽ:êóæG°*$ùÃŽ:êóæG°!):êóæG°°!:êóæG°!:êó!õÍ.ñ,Í$(Í!Ô*Í!$Í:öúg.&Í>-Í$>À.Í$Í.É}Í$ |óÓ™yö€Ó™ûÉ>ͤ8øÉíK ùo&))) @üÅÕå:ùÍÒ#áÑÁ#ïÉåõ!dÍ´ ñáÉRIGHTCåõ!vÍ´ ñáÉLEFTCåõ!‡Í´ ñáÉUPCåõ!–Í´ ñáÉTUPCåõ!¦Í´ ñáÉDOWNCåõ!·Í´ ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!" Í´ ñáÉSTORECåõ!4 Í´ ñáÉSETATRåõ!F Í´ ñáÉREADCåõ!W Í´ ñáÉSETCåõ!g Í´ ñáÉNSETCXåõ!y Í´ ñáÉGTASPCåõ!‹ Í´ ñáÉPNTINIåõ! Í´ ñáÉSCANRåõ!® Í´ ñáÉSCANL>#Ó.ÍÀ >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍÈ ñÍÈ É|ÍØ }ÍØ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Õ%ÃVxÓ¨y2ÿÿÙ!óùÍSÍ—ÍÞ͸ûÍóO!€ÅåÕ:ÁüÍÒ#áÑÁ¾ ë#ìÝ!€ý*ÀüÍG$>2êó2ëóͦ!i%Í3ó>Ó™>ŽÓ™¯Ó™ö@Ó™>vÓ˜¯Ó™!üúÓ™Û˜þv(ËÎËÖ¯Ó™>ŽÓ™ûxÍ/Í¿#>2ëó>2êó>2éó>2¯óͦÝ!AÍõ!i%Í3Í^ÍÚþÍ=>2™ý¯2)ûÍËþ!&Í3ÃC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@Í¥̲!€Í¥̲Ë(ÆËg(äá#<æ ÛÉGÍÒ##õxÍÒ##Wñ_xÉåÍ•!ABÍÆxáÉåõ!%Í3ñõGæÆ0ÍYxËx(>.ÍYxæÆ0ÍY> ÍY> ÍYñá##Í4(OÕÝáõýáÛ™·úòõåÍG$óáñÍ4(ËéÍ4(ËñÍ4(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍ•z³xÁÉ!Éü@~Ë(å!°%Í3á#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°>!çÿwèÿí°>2çÿ>2èÿ!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!N%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!Y"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉ!ÁüÅåy¶Ë Í ÍáÁØ# é¯2øúÉÅÍ ÁØÆöÉO2øú!Í&þC #Í&þDy7È·ÉyÅåÍÒ#áÁÉvýÉ:¯üþ0 ~·ÈÍY#÷~·ÈÝ!‰Íõ#ó :¯ü·À(:°óþ)ØPÉÅõÍP:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!Í´ ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!×Í´ ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃG$>ÍÃîåõ!Í´ ñáÉINIFNKåõ!Í´ ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõ͸ñõÍÍÍ:Ýó2aöñÁÑáÉÍÅÐ(õ:§ü·Âbñþ 8*þʪÍbÍ`*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍÃÓ !<Ã> Í—:Ýóæþ òÉ*Üó:±ó,½0 åÍêÍ5á-"ÜóÉ!"ÜóÉÍÓ*ÜóåÍ:Üó!±ó¾0ÍÓÍÓïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!`Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍ!±û:Üó_w:°ó ÍbÃŽÍ*ÜóåE:±ó2ÜóG:ÜóÍbë=2ÜóÍbÍsðá"Üó&:±óW•=O!±ûëbk+í¸ÃÓÍ*ÜóåE:±óG:ÜóÍbë<2ÜóÍbÍsðÍÓá"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOþ)8 (Í:°óÖ(OÍÁñÉåÕÅüÍ­ÁÑÕÅ!üÍÌÁá ëá É:ÝóþÈ> Í—Ã7:©üþÀ:¯üþÐ:ÌûÍbÃ`:©üþÀ:¯üþÐÍbÍZ2Ìû§_ËËËËË˯!¯ü¾ *·ó*ÁóåüÍ­:ªüþ !ü!ü~/w#úáøë!üÍÌÍb>ÿÃ` 7 Ä Ó ê 3 7LUj3E3KÓJñlÐLñM5Y±ALBUCºDÇHêx©y­ͶÿõÍÙ8Í·(öñ > ͪ¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§Ê>2ÝóÃ? Íàý!üÍ3*Ýó"Êû°û&}wÍ4þʘþ 0!™Í¯2¨ü2ªüãõ:¨ü§Ä?ñßÉ͸*Üó"¼ö> 2§öÍbÍZþ (*Üó:°ó¼ &±û~·(:§öÍbÍ`*¼ö"ÜóÉÃÍ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉŠ‹Ž‘’•–—ÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!Í´ ñáÉISCNTCÃõåõ!Í´ ñáÉBEEP"ÜóÉåõ!)Í´ ñáÉFNKSBåõ!:Í´ ñáÉERAFNKåõ!LÍ´ ñáÉDSPFNK:¯üþØ:°üͽý·Êæåõ!rÍ´ ñá7ÉTAPIONåõ!…Í´ ñá7ÉTAPINåõ!–Í´ ñáÉTAPIOFåõ!©Í´ ñá7ÉTAPOONåõ!¼Í´ ñá7ÉTAPOUTåõ!ÎÍ´ ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>Í<Í<Í<¸>ÍÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Í'/æå!¦_~áÁ§ÉåÕ=(Ëó>óÍû濳_>Í>óÍû/æ!–O ~ÑáÁ§É>Á§Éþ0· >Í'öþ<Éó=ÕGæ(L>Í濳_>Íxæ( >óÍûÑ (>ÿɯÉåõ!Í´ ñá¯ÉGTPADåõ!Í´ ñáÉGTPDLÍNF#~##¦o&ÉÍN~2öó¯Í¶/æ2èóÍ*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍ>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!ž&!Î&ÍJ!¾'ݾÄj/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(ÃÉyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍÖÑôÑÁá ~§(åÍÖáñÃ`*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍõÝáÉÙõÅÕåíWõÙýå:øúõýáÍG$ýáÙñâûáÑÁñÙÉþÍÚþ&ÃVåõ!6Í´ ñáç%ÃVCALBASí[ÜóÕ"ÜóÍÓÑíSÜóÉÛô/É/ÓôÉÛ™!æ' ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!Î%~Ó˜#~§ ùÓ˜§ ùÃüÍ $_EñæÍ $GÛ¨W£°áÍ€ó{ÑõËzÄ-%ñáÁÉÈüÉåWÕ§óüñ$ÁáÕå|æoG>üÍ $_EzæÍ $GÛ¨W£°áYÍ…óÑåËzÄ-%áÉÙóýåñÝåáW§üñ$ÕzæGüÝåñæ‡(ËË= ù!z$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ-%ÙÉóåoæG>«ÆUòŽ$W|ægG>Àòž$_/Oz£G}§òé$æåÅG>«ÆUò¸$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: BR Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-=\[];'`,./abcdefghijklmnopqrstuvwxyz)!@#$%^&*(_+|{}:"~<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ ¬«ºï½ôûìñ »óòļÇÍÜÆÝÈ ÂÛÌÒÀÏ ýüõð÷®¯öþúÁÎÔÖßÊÞÉ ÓÃ×Ë©ÑÅÕÐùªøëŸÙ¿›˜àáç‡îéíÚ·¹å†¦§„—‹Œ”±¡‘³µæ¤¢£ƒ“‰–‚•ˆŠ …Ø­ž¾œâ€è궸䨎™š°’²´¥ã  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í´ > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í´ ñáÉBASIC statements are not implemented yetÍ3Éåõ!"}Í´ ñáÉunknown@7D179}Ý!‰ÍõÉÝ!…Ãõåõ!~Í´ ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_BR/hardwareconfig.xml0000644000175000017500000000604612262345041024441 0ustar manuelmanuel00000000000000 C-BIOS MSX2+ 2010 An MSX2+ machine using C-BIOS, with MSX-MUSIC, and Brazillian style settings like 60Hz interrupt frequency. MSX2+ largest 4eac4b402e3b0f37521676aeab0685c900ae0ec6 roms/cbios_main_msx2+_br.rom 2fcb40413e7d373f0f2dbdc815ce18746ddf3684 roms/cbios_sub.rom 5c5eb001e6a1fe29edb7abd428a3967bb388e5db roms/cbios_music.rom 9000 512 16000 false int true false false V9958 128 YM2149 21000 cbios-msx2+.cmos true openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_JP/0000755000175000017500000000000012262345041020734 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_JP/roms/0000755000175000017500000000000012262345041021714 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_JP/roms/cbios_sub.rom0000644000175000017500000004000012262345041024375 0ustar manuelmanuel00000000000000CD~·ÉÉÉÉ|ºÀ}»ÉÉÉÉÉÉÉÉÉÉõÅÕåÙõÅÕåÝåýåÝ!8ý*ÀüÍÅýáÝááÑÁñÙáÑÁñíMÉÉÉÉÉÍÖýíEÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃòûÃûÃü ûÃsûÃyûÃgûÊûÃ…û× û×ûé ûÃê ûÃÙ ûÃÉ ûú ûÃÐûÃáûéûà ûóûÃÅûÃûÃ`ûÃ¥ûÃøûÃPûÃûòûÃfûÃûÃûÃ%ûÃu ûÃÊ ûÃÄ ûÃûÃn ûÃMÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃÿûÃÒ ÉÉÉÉÉÉÉÉûÃaûüûÃØûÃïûÃÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃ|ûÃŒûÞûðÉÉÉÉûÃûÃûÃ’ûÃòûÃûÃûÃ%ûÃ6ÉÉÉÉûÃHÉÉÉÉûÃOÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃaûÃ~ÉÉÉO þ#(##øÉN#fié>#Ó.Í">Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ*ñÍ*É|Í:}Í:ÉÅåõW§óüoñáåÕåõ|æoG>ü͉_Eñæ͉GÛ¨W£°áÍ€ó{ÑõËzÄ«ñáÁÉÈüÉåWÕ§óüoÁáÕå|æoG>ü͉_Ezæ͉GÛ¨W£°áYÍ…óÑåËzÄ«áÉÙóýåñÝåáW§üoÕzæGüÝåñæ‡(ËË= ù!øåÛ¨õ¡°ÙÃŒóÙóÑËzÄ«ÙÉóåoæG>«ÆUò W|ægG>Àò_/Oz£G}§ògæåÅG>«ÆUò6£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝé:àóæ¿GÍÿÉ:àóö@GÍÿÉó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ0Û˜ÉõÍBñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍB͈ xAO ñÓ˜ü ùÉ:¯üþ0Í0Í~ åë xA<˜í²= ûáÉë:¯üþ0ÍB͈ ë xA<˜í³= ûëÉþ Ð!¼ÃÅ`¥s ý [ ·  !èÿ:¯üþ0˾Ëþ¯2ìÿ¯2öÿó!ßó™€í£xíQ· ÷>Ó™>‘Ó™!çÿ›í³###>Ó™>‘Ó™›í³ûÃó:¯üþ(:=õ:éóæðo:ëóµGÍÿñÀ:éóæð!êó¶*¿ó õÍBñÓ˜õ x± ÷ñÉ:ëóGÃÿ:¯ü·ÈÍ:¯üþ8*(ù%%:éóæÍi *&ù¯Íi É:¯üþ8ÙÑ*(ù͈ :éóW ó{Ó˜>Ó˜yÓ˜ Í%0 zÓ˜èûÉÍç>2¯ü2°ü¯2õú2öú:®ó2°ó>2Üó2Ýó*³ó""ù*·ó:°óþ)8!"$ù*¹ó"(ù*»ó"&ùÍÍøÍ\ Í ÃÎÍç>2¯ü2°ü>2Üó2ÝóÍ*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍ\ :¯ó2°ó¯2õú2öúÍP͛͞ ÃÎÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù¯2õú2öúÍÍ›ͽ ÃÎÍç>2¯üÍ*Ñó""ùÍB¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ù¯2õú2öúͲÍ›ÍÓ ÃÎ:°óþ)0(:ßóæñGÍÿ:àóæçöG Íÿ³ó¯Íã ¯ÍãÉ:ßóæñöGÍÿ:àóæçöG Íÿ"ù>Íã ¯ÍãÉ:ßóæñGÍÿ:àóæçG Íÿ½ó¯Íã¯Íã¯Íã¯Íã¯ÍãÉ:ßóæñöGÍÿ:àóæçG ÍÿÇó¯Íã>Íã>Íã¯Íã¯ÍãÉ:ßóæñGÍÿ:àóæçöG ÍÿÑó¯Íã¯Íã¯Íã¯Íã¯ÍãÉÕõ! Fë~#fo)üGñ°GÍÿÑ É  &o)))Í%0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍu í[¹üíK·üÍ :éó2òó**ùíKËó @ü:¹üæO Í«ð :¹ü/æOÍ«*·ü "·üñÁÑáÉ:·üæõÅÕåÍ4 :·üæ(:,ù/2,ùá ÑÁñÍ4 :,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍ"õæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í(Ýñæð°Í(ÚAŽÓ™á}Ó™ÉÍ~ Û˜Éõ͈ ñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™ÍÿàÍÿÍÿ€ÍÿÍÿÍÿ>2Üó2ÝóÍž >!ÍV>õ!  ÍVõÍÿ!¿ý*ÀüÝ!\ÍÅûÉÍÿ;ÍÿÍÿÉ!¿í[$ùý*ÀüÝ!\ÍÅûÉÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù:ßóæñöGÍÿ:àóæçG ÍÿÍÿÇó¯Íã>Íã>Íãû >ÍãÏó¯Íã!éÿ6#6Í›Íÿͽ ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçö G Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Íç ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçG Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Í÷ ÃÎÍç>2¯üÍ:ßóæñö GÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÍç>2¯üÍ:ßóöGÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÀ:¯üþ Ðå!} ÍáÉ ž ½ Ó ½ ç ÷  :°óþ(À8€*"ù> ÍV!"Üó}!²ûw³ûí°É¯*$ùoÅÍVÁ:êó*ÉóÃV:êóæG°*$ùÃV:êóæG°!):êóæG°°!:êóæG°!:êó!õÍl ñ,Íb (Í\ !Ô*Í\ !$Í\ :öúg.&Í\ >-Íb >À.Íb Íl É}Íb |óÓ™yö€Ó™ûÉ>ÍÒ 8øÉíK ùo&))) @üÅÕå:ùÍPáÑÁ#ïÉåõ!¢ ÍñáÉRIGHTCåõ!´ ÍñáÉLEFTCåõ!Å ÍñáÉUPCåõ!Ô ÍñáÉTUPCåõ!ä ÍñáÉDOWNCåõ!õ ÍñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!`ÍñáÉSTORECåõ!rÍñáÉSETATRåõ!„ÍñáÉREADCåõ!•ÍñáÉSETCåõ!¥ÍñáÉNSETCXåõ!·ÍñáÉGTASPCåõ!ÉÍñáÉPNTINIåõ!ÛÍñáÉSCANRåõ!ìÍñáÉSCANLåõ!ýÍñáÉDOGRPHÅÕåÍu :éó2òó*·ü"fõ "·ü:öú*¹üg"hõíCjõíClõ!@üË:òó8:êó2nõÍÂ:ûæö°íy>¬Ó™>‘Ó™û!@üË:òó8:êóÓ› ñ#ìáÑÁÉåõ!~ÍñáÉMAPXYCåõ!ÍñáÉTRIGHTåõ!¢ÍñáÉTRIGHT:öúWÕíCfõíShõ*³ü§íB"jõ!"lõ¯2oõ:òó2nõÍý*µü:öúg"hõÍýÑíShõ>2oõ:µü“2jõÍý*³ü"fõÍýÉÍÂ:ûæöpíyûÉ*µüç0ë§íR#"lõ:öúWíShõÅÑ*³üç0ë§íR#"jõíSfõ¯2oõ:òó2nõÍÂ:ûæö€íyûÉåõ!ZÍñáÃn CLRTXTåÕÅ*"ùËË:õú„öGÍÿ:¯üþ :åóæG:õú°GÍÿ:õúO:¯üþ Ë!¯*(ù)±o>´GÍÿE ÍÿÁÑáÉåÍ'͈ Íÿ !\~Ó˜#ÓšøáÉåÍ'Í~ áÍÿ Û˜ÓšúÉåõÍ'ñ‡O Í~ Û˜GÛ˜OáÉõÅåÍ'z‡O ͈ áBÍÿÁñÓšÓ˜{ÓšÓ˜É:¯ü·Ì>ÍÒ 8øó> Ó™>‘Ó™›!bõí³Éx±7Èz³7Èå!:¯üæþ:¯ü $·íBáØå!Ô·íRáÉíKjõí[lõÍÜØÍÂ~æöíyû·É*bõN#F#íCjõ^#V#íSlõÍÜØþ( þ(N¯CËËø2nõBÅåÍÂ~æö°íy>¬Ó™>‘Ó™ûáÁ>ÍÒ ËGÈË(ôx§(ůCËËøÓ›yÁOî#NBÙ*fõíKjõí[lõq#p#s#r#ÍÜØþ( þ(åÍÂ~æ> íyûáB>ÍÒ Ë ËG óÅ>ÍÒ CËü±ÁOãq#>ÍÒ 8ÖÉåõ!ýÍñáÉBLTVDåõ!ÍñáÉBLTDVåõ!ÍñáÉBLTMDåõ!0ÍñáÉBLTDMåõ!AÍñáÉNEWPADͳͼÉåõ!ZÍñáÉKNJPRTÅ> Ó´yæGÛµ°ÁÓµyæÓ´ÛµæÉÅGÅ> Ó´yæGÛµ°ÁÓµyæÓ´xæÓµÁÉopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_JP/roms/cbios_main_msx2+_jp.rom0000644000175000017500000010000012262345041026242 0ustar manuelmanuel00000000000000óà ¿˜˜Ã’ÃÒ#äÃ$ÃÀÃG$ÃÆÆ$ÃÌÃÞÚÃóÃýÃÃ"Ã.ÃZÃ`ÃhÃzÃŽíÃÌÃëÃôÃÃ>ÃæïøÃÁÃÃHÃ{ÃÑÃäÃîÃùÃîÃÃÃÃ!Ã4ÃYÃ÷ÃÅÃêÃÃÿÃÙÃõÃà Ã2ÃÃÃ/ÃAÃSÃCöÃûà ÃfÃyËÃðÃÃÃÕÃÃ-ÃYÃkÃ|ËÛìþÃÎà à Ã) Ã; ÃL Ã\ Ãn À Ã’ ã Ã[ÃÃÃ!Ã$Ã'Ã6Ã:Ã>ÃgÃyÃ}ÓÃ&ÃïÃõÃÞÃ5Ã=Ã;ÃPÃZÖÜÃNÃRO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóyþ8!ßÿþ8 ( þ0!áÿx wáÉÍhÛ˜ÉõÍzñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍzÍZ xAO ñÓ˜ü ùÉ:¯üþ0ÍhÍPåë xA<˜í²= ûáÉë:¯üþ0ÍzÍZë xA<˜í³= ûëÉÝåÝ!ÑÃï:¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍzñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍh:¯üþ8*(ù%%:éóæÍ;*&ù¯Í;É:¯üþ8ÙÑ*(ùÍZ:éóW ó{Ó˜>Ó˜yÓ˜ Íî0 zÓ˜èûÉÝåÝ!ÕÃïÝåÝ!ÙÃïÝåÝ!ÝÃïÝåÝ!áÃï:°óþ)0(:ßóæñGÍ.:àóæçöG Í.³ó¯Í¬ ¯Í¬É:ßóæñöGÍ.:àóæçöG Í."ù>ͬ ¯Í¬É:ßóæñGÍ.:àóæçG Í.½ó¯Í¬¯Í¬¯Í¬¯Í¬¯Í¬É:ßóæñöGÍ.:àóæçG Í.Çó¯Í¬>ͬ>ͬ¯Í¬¯Í¬É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í¬¯Í¬¯Í¬¯Í¬¯Í¬ÉÕõ!Ê Fë~#fo)üGñ°GÍ.Ñ É  &o)))Íî0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ(+þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÝåÝ!‰ÃïÉñåÕÅõÍ7í[¹üíK·üÍÎ:éó2òó**ùíKËó @ü:¹üæO Í}ð :¹ü/æOÍ}*·ü "·üñÁÑáÉ:·üæõÅÕåÍ:·üæ(:,ù/2,ùá ÑÁñÍ:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍZõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í`Ýñæð°Í`ÚAŽÓ™á}Ó™ÉÍPÛ˜ÉõÍZñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍc>!ÍŽ>õ!  ÍŽõÍ.!¿ÍÌÉÍ.;Í.Í.É!¿í[$ùÃÌÀ:¯üþ Ðå!BÍáÉTc•©¹ÌÜ:°óþ(À8€*"ù> ÍŽ>!²ûw³ûí°Ãê¯*$ùoÅÍŽÁ:êó*ÉóÃŽ:êóæG°*$ùÃŽ:êóæG°!):êóæG°°!:êóæG°!:êó!õÍ.ñ,Í$(Í!Ô*Í!$Í:öúg.&Í>-Í$>À.Í$Í.É}Í$ |óÓ™yö€Ó™ûÉ>ͤ8øÉíK ùo&))) @üÅÕå:ùÍÒ#áÑÁ#ïÉåõ!dÍ´ ñáÉRIGHTCåõ!vÍ´ ñáÉLEFTCåõ!‡Í´ ñáÉUPCåõ!–Í´ ñáÉTUPCåõ!¦Í´ ñáÉDOWNCåõ!·Í´ ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!" Í´ ñáÉSTORECåõ!4 Í´ ñáÉSETATRåõ!F Í´ ñáÉREADCåõ!W Í´ ñáÉSETCåõ!g Í´ ñáÉNSETCXåõ!y Í´ ñáÉGTASPCåõ!‹ Í´ ñáÉPNTINIåõ! Í´ ñáÉSCANRåõ!® Í´ ñáÉSCANL>#Ó.ÍÀ >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍÈ ñÍÈ É|ÍØ }ÍØ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Õ%ÃVxÓ¨y2ÿÿÙ!óùÍSÍ—ÍÞ͸ûÍóO!€ÅåÕ:ÁüÍÒ#áÑÁ¾ ë#ìÝ!€ý*ÀüÍG$>2êó2ëóͦ!i%Í3ó>Ó™>ŽÓ™¯Ó™ö@Ó™>vÓ˜¯Ó™!üúÓ™Û˜þv(ËÎËÖ¯Ó™>ŽÓ™ûxÍ/Í¿#>2ëó>2êó>2éó>2¯óͦÝ!AÍõ!i%Í3Í^ÍÚþÍ=>2™ý¯2)ûÍËþ!&Í3ÃC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@Í¥̲!€Í¥̲Ë(ÆËg(äá#<æ ÛÉGÍÒ##õxÍÒ##Wñ_xÉåÍ•!ABÍÆxáÉåõ!%Í3ñõGæÆ0ÍYxËx(>.ÍYxæÆ0ÍY> ÍY> ÍYñá##Í4(OÕÝáõýáÛ™·úòõåÍG$óáñÍ4(ËéÍ4(ËñÍ4(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍ•z³xÁÉ!Éü@~Ë(å!°%Í3á#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°>!çÿwèÿí°>2çÿ>2èÿ!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!N%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!Y"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉ!ÁüÅåy¶Ë Í ÍáÁØ# é¯2øúÉÅÍ ÁØÆöÉO2øú!Í&þC #Í&þDy7È·ÉyÅåÍÒ#áÁÉvýÉ:¯üþ0 ~·ÈÍY#÷~·ÈÝ!‰Íõ#ó :¯ü·À(:°óþ)ØPÉÅõÍP:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!Í´ ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!×Í´ ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃG$>ÍÃîåõ!Í´ ñáÉINIFNKåõ!Í´ ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõ͸ñõÍÍÍ:Ýó2aöñÁÑáÉÍÅÐ(õ:§ü·Âbñþ 8*þʪÍbÍ`*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍÃÓ !<Ã> Í—:Ýóæþ òÉ*Üó:±ó,½0 åÍêÍ5á-"ÜóÉ!"ÜóÉÍÓ*ÜóåÍ:Üó!±ó¾0ÍÓÍÓïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!`Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍ!±û:Üó_w:°ó ÍbÃŽÍ*ÜóåE:±ó2ÜóG:ÜóÍbë=2ÜóÍbÍsðá"Üó&:±óW•=O!±ûëbk+í¸ÃÓÍ*ÜóåE:±óG:ÜóÍbë<2ÜóÍbÍsðÍÓá"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOþ)8 (Í:°óÖ(OÍÁñÉåÕÅüÍ­ÁÑÕÅ!üÍÌÁá ëá É:ÝóþÈ> Í—Ã7:©üþÀ:¯üþÐ:ÌûÍbÃ`:©üþÀ:¯üþÐÍbÍZ2Ìû§_ËËËËË˯!¯ü¾ *·ó*ÁóåüÍ­:ªüþ !ü!ü~/w#úáøë!üÍÌÍb>ÿÃ` 7 Ä Ó ê 3 7LUj3E3KÓJñlÐLñM5Y±ALBUCºDÇHêx©y­ͶÿõÍÙ8Í·(öñ > ͪ¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§Ê>2ÝóÃ? Íàý!üÍ3*Ýó"Êû°û&}wÍ4þʘþ 0!™Í¯2¨ü2ªüãõ:¨ü§Ä?ñßÉ͸*Üó"¼ö> 2§öÍbÍZþ (*Üó:°ó¼ &±û~·(:§öÍbÍ`*¼ö"ÜóÉÃÍ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉŠ‹Ž‘’•–—ÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!Í´ ñáÉISCNTCÃõåõ!Í´ ñáÉBEEP"ÜóÉåõ!)Í´ ñáÉFNKSBåõ!:Í´ ñáÉERAFNKåõ!LÍ´ ñáÉDSPFNK:¯üþØ:°üͽý·Êæåõ!rÍ´ ñá7ÉTAPIONåõ!…Í´ ñá7ÉTAPINåõ!–Í´ ñáÉTAPIOFåõ!©Í´ ñá7ÉTAPOONåõ!¼Í´ ñá7ÉTAPOUTåõ!ÎÍ´ ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>Í<Í<Í<¸>ÍÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Í'/æå!¦_~áÁ§ÉåÕ=(Ëó>óÍû濳_>Í>óÍû/æ!–O ~ÑáÁ§É>Á§Éþ0· >Í'öþ<Éó=ÕGæ(L>Í濳_>Íxæ( >óÍûÑ (>ÿɯÉåõ!Í´ ñá¯ÉGTPADåõ!Í´ ñáÉGTPDLÍNF#~##¦o&ÉÍN~2öó¯Í¶/æ2èóÍ*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍ>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!ž&!Î&ÍJ!(ݾÄj/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(ÃÉyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍÖÑôÑÁá ~§(åÍÖáñÃ`*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍõÝáÉÙõÅÕåíWõÙýå:øúõýáÍG$ýáÙñâûáÑÁñÙÉþÍÚþ&ÃVåõ!6Í´ ñáç%ÃVCALBASí[ÜóÕ"ÜóÍÓÑíSÜóÉÛô/É/ÓôÉÛ™!F( ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!Î%~Ó˜#~§ ùÓ˜§ ùÃ|D|D|DŒ ¤¤ 0H„ø8T’|8T’8Tº8Tþ|þ|DD|DD|@||Püü¤¤ü„„„î¤þ¬ä (D‚|$DH$ÎUìbÒD| |D|D|pþü@@|D„ˆ|((HHŒÿÿðÿÿððB$$B|(H„|T|TT”0ll$$~$~$$>TT>>"T(%B8DD(2J|`` T88T|`` ~`` @8LTTd8088D8@|x8xDDD||@xx8@xDD8|D8D8DD88DD<8 @ ||  8DüÍ $_EñæÍ $GÛ¨W£°áÍ€ó{ÑõËzÄ-%ñáÁÉÈüÉåWÕ§óüñ$ÁáÕå|æoG>üÍ $_EzæÍ $GÛ¨W£°áYÍ…óÑåËzÄ-%áÉÙóýåñÝåáW§üñ$ÕzæGüÝåñæ‡(ËË= ù!z$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ-%ÙÉóåoæG>«ÆUòŽ$W|ægG>Àòž$_/Oz£G}§òé$æåÅG>«ÆUò¸$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: JP Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-^\@[;:],./_abcdefghijklmnopqrstuvwxyz0!"#$%&'()=~|`{+*}<>?_ABCDEFGHIJKLMNOPQRSTUVWXYZ à „‚…€ƒ    à „‚…€ƒ   üçì‘“”•ôõöîí°Þßú™ñèùòû᚟œ’ê—˜æïéøóð÷žàä–åëã›ýâÜÇ̱³´µÔÕÖÎͰÞßÚ¹ÑÈÙÒÛÁº¿¼²Ê·¸ÆÏÉØÓÐ×¾À½Ä¶ÅËû݆‡‰Š‹ŒŽ¢ƒ„…ˆ¦§©ª«¬­®¢£¤¡¥¨¯  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í´ > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í´ ñáÉBASIC statements are not implemented yetÍ3Éåõ!"}Í´ ñáÉunknown@7D179}Ý!‰ÍõÉÝ!…Ãõåõ!~Í´ ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_JP/roms/cbios_music.rom0000644000175000017500000004000012262345041024724 0ustar manuelmanuel00000000000000ABAPRLOPLLÃ%AÃMAÃuAÃAÃÄAÃìAÃBåõ!0AÍ#Ó.ÍHB>Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍPBñÍPBÉ|Í`B}Í`BÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_JP/roms/cbios_logo_msx2+.rom0000644000175000017500000004000012262345041025570 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÍzØ>€Í}À!RƒÍØ‚!"êó>Í_!€ÍEÍA!"êóÍb:èÿæG ÍG>Ý!1Í_ËG óÕ!qí°á Àwëó> Ó™>‘Ó™å›!qí³á>¬Ó™>‘Ó™ûÿí³>Ý!1Í_ËG(í³ï À!€í°!°"·ü!a"¹ü>2éó>2û!Ò‚ÍT2ûÍD !ÀæðO~æð¹(0ÆÖO~æ±wæO~æ¹(0<=O~æð±w#O~¹(0<=w#¿!ÀÍEvý  !À¾  #ø !À‚^#V#ÅåëÍEáÁvvðÉó¯Ó™>Ó™š í³ûÉ:¯üþ0 ~·ÈÍ¢#÷~·ÈÝ!‰Í_#óUð''''''''''''''''3Uwtcsrrrpp'3Uwtcsrrrpw'3Uwtcsrrrwp'3Uwtcsrrwpp'3Uwtcsrwrpp'3Uwtcswrrpp'3Uwtcwrrrpp'3Uwwcsrrrpp'3UwtwsrrrppÀà‚ ‚@‚`‚€‚ ‚ V0.25Ù€ÙÙÍ.ƒÙ8í õ~#·(0Ù_P0Í.ƒËÍ.ƒËÍ.ƒËÍ.ƒ8Ë»Í;ƒåÙåÕÙbkÁíBÁí°áÀÍ:ƒØåÙåÙÁbk+í°á¯Ë!ÀÙ~#ÙOË! ÉÙ!Í.ƒ0øÍ.ƒíjù#ÙÉ~3Á@ý¢Cy6©™4}cû:™šj»º…$“ƒ¿a~ªª»ª©€?®Ç~«~Œƒ¬€Y~ï€×g~}¬€[~âªÙ‰j€+…~˜CèrºªµýW‰ˆcº“PzùüïföÙ¶÷a 8˜š€Mû–9¿ö<‘ˆ€Ò©Ž7õ_ ”™ö~€ž&ŽþŒ9›á¹l戗€ßü¤™ìóë‚8Š»€_wòcþ¸êõ–€f““Aþ€®Š1ïüŒþ|³‰‰øŸ“Ï™ÌWéìþé9œ~ÁÉ“ý´þ|+fNá|6Á¹œþ!WwvNð¾5?à¥þâ|Vgå<Ì(3ÿó|fGUVòdCÌþüéwcÿý93þ?“µ:î3kþ/õ”ýÕý¹õïyÄŠw çΰ:z?c!8óÎDw{gKW”Q—\#vǯÈ'C>þ§ã·R•gç~xfÇ€Y+#yþSÝ–ÙézÈYgeUÜn8vcç9FV¡#f‘Œ+zUþ8Cø’VÜçgsfþE˜ÞHg™åf‡-ÓÿèÝýÿ=ÉÒfã_of1ë¾'< Û­Ïøi;þø¶ëYðfðŽ‹c™ËØ#C=þ—ÜûúKñ^fNf£þÒ™7z;—èS þä½Ü8;þcÝúߨ¸®×ý†ÿ8÷ù~f¥‰)Gfí×þËð_úf6§ê{©¯Üÿ‘WîVØêÉþvž”iDòNÕ>seþ×ø þù>·sÄVÞ ¿JþþOÎÓ¡ý}¿):ü€ûé.Óò¢Gý—”5ø©8ÿé“ûŽ3ûôÒ”€Züÿ|¨4ΞSf£EfC¯i*döODFd~Cäÿ| 6ÿÒWvÿ>-w—­ÛÏþfåûëÁùóÿòHåe—þ|¨%éEVSþ.òêÌ­üÿû~æ4FPpD'VUêÕfþ?ð2ÿïþ)þÝS™´þ¦ù|rÿùh®ÿüþ’eþGÿ)4Uþ„TCÿ~é3ŽÿÏ·e6éÍ€uS˜{|¤[ÿä÷ÿÉëe7UeDUgU˜eþ|˜“áøoŠ¿Ï*ü¶eUŽ…$€eUS¥ÊUgþ’¯GDä«8€âÍÍr•ý§S…(ˆÉ€6áê%ÇgDÔNÿøV€ògbŒãŒD~RdÔ͉ ¼ìàUŽ»šàÏ0Þó§"õqUbíÄ€EæfÎ ¼ü~?8€_ͺ9CËÇgê~73ä߀Íü7áÛ€ƒãûk€mîý­€8?ï³R­:Šók€>ÿìç€é:?ÿþ~€~dÿ–ÿøS€¤9Ÿé|QþüâJ€º™jê>~ë•öÿ€^(þþñ/e€ÁÿþÆ~/‚$€¸½ÿ~–ÿÿüü€®)šÿðæ?ô þ})ƒ8¯ÿãcôÿô΃$€|‡4"î~3ƒ€>A3@ý"D€>_~éå€c€â?30~¦–€#€^ª}C¯34~úˆ€ì8ªKªƒ#úªó~úȀ爃Lc¯D~úÈ€êƒSƒ4D~úø€ï¿~ì-…ž@ícûú/ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+_JP/hardwareconfig.xml0000644000175000017500000000613212262345041024443 0ustar manuelmanuel00000000000000 C-BIOS MSX2+ JP 2010 An MSX2+ machine using C-BIOS, with MSX-MUSIC, a Japanese keyboard layout and 60Hz interrupt frequency. MSX2+ largest 366c76ded9e9fb8253397ade83acc10ca3a35fcd roms/cbios_main_msx2+_jp.rom 2fcb40413e7d373f0f2dbdc815ce18746ddf3684 roms/cbios_sub.rom 5c5eb001e6a1fe29edb7abd428a3967bb388e5db roms/cbios_music.rom 9000 512 16000 false jp_ansi true true false V9958 128 YM2149 50on 21000 cbios-msx2+_jp.cmos true openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_JP/0000755000175000017500000000000012262345041020661 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_JP/roms/0000755000175000017500000000000012262345041021641 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_JP/roms/cbios_sub.rom0000644000175000017500000004000012262345041024322 0ustar manuelmanuel00000000000000CD~·ÉÉÉÉ|ºÀ}»ÉÉÉÉÉÉÉÉÉÉõÅÕåÙõÅÕåÝåýåÝ!8ý*ÀüÍÅýáÝááÑÁñÙáÑÁñíMÉÉÉÉÉÍÖýíEÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃòûÃûÃü ûÃsûÃyûÃgûÊûÃ…û× û×ûé ûÃê ûÃÙ ûÃÉ ûú ûÃÐûÃáûéûà ûóûÃÅûÃûÃ`ûÃ¥ûÃøûÃPûÃûòûÃfûÃûÃûÃ%ûÃu ûÃÊ ûÃÄ ûÃûÃn ûÃMÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃÿûÃÒ ÉÉÉÉÉÉÉÉûÃaûüûÃØûÃïûÃÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃ|ûÃŒûÞûðÉÉÉÉûÃûÃûÃ’ûÃòûÃûÃûÃ%ûÃ6ÉÉÉÉûÃHÉÉÉÉûÃOÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃaûÃ~ÉÉÉO þ#(##øÉN#fié>#Ó.Í">Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ*ñÍ*É|Í:}Í:ÉÅåõW§óüoñáåÕåõ|æoG>ü͉_Eñæ͉GÛ¨W£°áÍ€ó{ÑõËzÄ«ñáÁÉÈüÉåWÕ§óüoÁáÕå|æoG>ü͉_Ezæ͉GÛ¨W£°áYÍ…óÑåËzÄ«áÉÙóýåñÝåáW§üoÕzæGüÝåñæ‡(ËË= ù!øåÛ¨õ¡°ÙÃŒóÙóÑËzÄ«ÙÉóåoæG>«ÆUò W|ægG>Àò_/Oz£G}§ògæåÅG>«ÆUò6£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝé:àóæ¿GÍÿÉ:àóö@GÍÿÉó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ0Û˜ÉõÍBñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍB͈ xAO ñÓ˜ü ùÉ:¯üþ0Í0Í~ åë xA<˜í²= ûáÉë:¯üþ0ÍB͈ ë xA<˜í³= ûëÉþ Ð!¼ÃÅ`¥s ý [ ·  !èÿ:¯üþ0˾Ëþ¯2ìÿ¯2öÿó!ßó™€í£xíQ· ÷>Ó™>‘Ó™!çÿ›í³###>Ó™>‘Ó™›í³ûÃó:¯üþ(:=õ:éóæðo:ëóµGÍÿñÀ:éóæð!êó¶*¿ó õÍBñÓ˜õ x± ÷ñÉ:ëóGÃÿ:¯ü·ÈÍ:¯üþ8*(ù%%:éóæÍi *&ù¯Íi É:¯üþ8ÙÑ*(ù͈ :éóW ó{Ó˜>Ó˜yÓ˜ Í%0 zÓ˜èûÉÍç>2¯ü2°ü¯2õú2öú:®ó2°ó>2Üó2Ýó*³ó""ù*·ó:°óþ)8!"$ù*¹ó"(ù*»ó"&ùÍÍøÍ\ Í ÃÎÍç>2¯ü2°ü>2Üó2ÝóÍ*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍ\ :¯ó2°ó¯2õú2öúÍP͛͞ ÃÎÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù¯2õú2öúÍÍ›ͽ ÃÎÍç>2¯üÍ*Ñó""ùÍB¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ù¯2õú2öúͲÍ›ÍÓ ÃÎ:°óþ)0(:ßóæñGÍÿ:àóæçöG Íÿ³ó¯Íã ¯ÍãÉ:ßóæñöGÍÿ:àóæçöG Íÿ"ù>Íã ¯ÍãÉ:ßóæñGÍÿ:àóæçG Íÿ½ó¯Íã¯Íã¯Íã¯Íã¯ÍãÉ:ßóæñöGÍÿ:àóæçG ÍÿÇó¯Íã>Íã>Íã¯Íã¯ÍãÉ:ßóæñGÍÿ:àóæçöG ÍÿÑó¯Íã¯Íã¯Íã¯Íã¯ÍãÉÕõ! Fë~#fo)üGñ°GÍÿÑ É  &o)))Í%0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍu í[¹üíK·üÍ :éó2òó**ùíKËó @ü:¹üæO Í«ð :¹ü/æOÍ«*·ü "·üñÁÑáÉ:·üæõÅÕåÍ4 :·üæ(:,ù/2,ùá ÑÁñÍ4 :,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍ"õæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í(Ýñæð°Í(ÚAŽÓ™á}Ó™ÉÍ~ Û˜Éõ͈ ñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™ÍÿàÍÿÍÿ€ÍÿÍÿÍÿ>2Üó2ÝóÍž >!ÍV>õ!  ÍVõÍÿ!¿ý*ÀüÝ!\ÍÅûÉÍÿ;ÍÿÍÿÉ!¿í[$ùý*ÀüÝ!\ÍÅûÉÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù:ßóæñöGÍÿ:àóæçG ÍÿÍÿÇó¯Íã>Íã>Íãû >ÍãÏó¯Íã!éÿ6#6Í›Íÿͽ ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçö G Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Íç ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçG Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Í÷ ÃÎÍç>2¯üÍ:ßóæñö GÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÍç>2¯üÍ:ßóöGÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÀ:¯üþ Ðå!} ÍáÉ ž ½ Ó ½ ç ÷  :°óþ(À8€*"ù> ÍV!"Üó}!²ûw³ûí°É¯*$ùoÅÍVÁ:êó*ÉóÃV:êóæG°*$ùÃV:êóæG°!):êóæG°°!:êóæG°!:êó!õÍl ñ,Íb (Í\ !Ô*Í\ !$Í\ :öúg.&Í\ >-Íb >À.Íb Íl É}Íb |óÓ™yö€Ó™ûÉ>ÍÒ 8øÉíK ùo&))) @üÅÕå:ùÍPáÑÁ#ïÉåõ!¢ ÍñáÉRIGHTCåõ!´ ÍñáÉLEFTCåõ!Å ÍñáÉUPCåõ!Ô ÍñáÉTUPCåõ!ä ÍñáÉDOWNCåõ!õ ÍñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!`ÍñáÉSTORECåõ!rÍñáÉSETATRåõ!„ÍñáÉREADCåõ!•ÍñáÉSETCåõ!¥ÍñáÉNSETCXåõ!·ÍñáÉGTASPCåõ!ÉÍñáÉPNTINIåõ!ÛÍñáÉSCANRåõ!ìÍñáÉSCANLåõ!ýÍñáÉDOGRPHÅÕåÍu :éó2òó*·ü"fõ "·ü:öú*¹üg"hõíCjõíClõ!@üË:òó8:êó2nõÍÂ:ûæö°íy>¬Ó™>‘Ó™û!@üË:òó8:êóÓ› ñ#ìáÑÁÉåõ!~ÍñáÉMAPXYCåõ!ÍñáÉTRIGHTåõ!¢ÍñáÉTRIGHT:öúWÕíCfõíShõ*³ü§íB"jõ!"lõ¯2oõ:òó2nõÍý*µü:öúg"hõÍýÑíShõ>2oõ:µü“2jõÍý*³ü"fõÍýÉÍÂ:ûæöpíyûÉ*µüç0ë§íR#"lõ:öúWíShõÅÑ*³üç0ë§íR#"jõíSfõ¯2oõ:òó2nõÍÂ:ûæö€íyûÉåõ!ZÍñáÃn CLRTXTåÕÅ*"ùËË:õú„öGÍÿ:¯üþ :åóæG:õú°GÍÿ:õúO:¯üþ Ë!¯*(ù)±o>´GÍÿE ÍÿÁÑáÉåÍ'͈ Íÿ !\~Ó˜#ÓšøáÉåÍ'Í~ áÍÿ Û˜ÓšúÉåõÍ'ñ‡O Í~ Û˜GÛ˜OáÉõÅåÍ'z‡O ͈ áBÍÿÁñÓšÓ˜{ÓšÓ˜É:¯ü·Ì>ÍÒ 8øó> Ó™>‘Ó™›!bõí³Éx±7Èz³7Èå!:¯üæþ:¯ü $·íBáØå!Ô·íRáÉíKjõí[lõÍÜØÍÂ~æöíyû·É*bõN#F#íCjõ^#V#íSlõÍÜØþ( þ(N¯CËËø2nõBÅåÍÂ~æö°íy>¬Ó™>‘Ó™ûáÁ>ÍÒ ËGÈË(ôx§(ůCËËøÓ›yÁOî#NBÙ*fõíKjõí[lõq#p#s#r#ÍÜØþ( þ(åÍÂ~æ> íyûáB>ÍÒ Ë ËG óÅ>ÍÒ CËü±ÁOãq#>ÍÒ 8ÖÉåõ!ýÍñáÉBLTVDåõ!ÍñáÉBLTDVåõ!ÍñáÉBLTMDåõ!0ÍñáÉBLTDMåõ!AÍñáÉNEWPADͳͼÉåõ!ZÍñáÉKNJPRTÅ> Ó´yæGÛµ°ÁÓµyæÓ´ÛµæÉÅGÅ> Ó´yæGÛµ°ÁÓµyæÓ´xæÓµÁÉopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_JP/roms/cbios_logo_msx2.rom0000644000175000017500000004000012262345041025442 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÀ!HƒÍ΂!"êó>Í_!vÍ;ÍA!"êóÍb:èÿæG ÍG>Ý!1Í_ËG óÕ!gí°á Àwëó> Ó™>‘Ó™å›!gí³á>¬Ó™>‘Ó™ûÿí³>Ý!1Í_ËG(í³ï À!ví°!°"·ü!a"¹ü>2éó>2û!È‚ÍJ2ûÍD–!ÀæðO~æð¹(0ÆÖO~æ±wæO~æ¹(0<=O~æð±w#O~¹(0<=w#¿!ÀÍ;vý –!À¾  #ø !¶‚^#V#ÅåëÍ;áÁvvðÉó¯Ó™>Ó™š í³ûÉ:¯üþ0 ~·ÈÍ¢#÷~·ÈÝ!‰Í_#óUð''''''''''''''''3Uwtcsrrrpp'3Uwtcsrrrpw'3Uwtcsrrrwp'3Uwtcsrrwpp'3Uwtcsrwrpp'3Uwtcswrrpp'3Uwtcwrrrpp'3Uwwcsrrrpp'3Uwtwsrrrpp¶Öö‚6‚V‚v‚–‚–V0.25Ù€ÙÙÍ$ƒÙ8í õ~#·(0Ù_P0Í$ƒËÍ$ƒËÍ$ƒËÍ$ƒ8Ë»Í1ƒåÙåÕÙbkÁíBÁí°áÀÍ0ƒØåÙåÙÁbk+í°á¯Ë!ÀÙ~#ÙOË! ÉÙ!Í$ƒ0øÍ$ƒíjù#ÙÉ~3Á@ý¢Cy6©™4}cû:™šj»º…$“ƒ¿a~ªª»ª©€?®Ç~«~Œƒ¬€Y~ï€×g~}¬€[~âªÙ‰j€+…~˜CèrºªµýW‰ˆcº“PzùüïföÙ¶÷a 8˜š€Mû–9¿ö<‘ˆ€Ò©Ž7õ_ ”™ö~€ž&ŽþŒ9›á¹l戗€ßü¤™ìóë‚8Š»€_wòcþ¸êõ–€f““Aþ€®Š1ïüŒþ|³‰‰øŸ“Ï™ÌWéìþé9œ~ÁÉ“ý´þ|+fNá|6Á¹œþ!WwvNð¾5?à¥þâ|Vgå<Ì(3ÿó|fGUVòdCÌþüéwcÿý93þ?“µ:î3kþ/õ”ýÕý¹õïyÄŠw çΰ:z?c!8óÎDw{gKW”Q—\#vǯÈ'C>þ§ã·R•gç~xfÇ€Y+#yþSÝ–ÙézÈYgeUÜn8vcç9FV¡#f‘Œ+zUþ8Cø’VÜçgsfþE˜ÞHg™åf‡-ÓÿèÝýÿ=ÉÒfã_of1ë¾'< Û­Ïøi;þø¶ëYðfðŽ‹c™ËØ#C=þ—ÜûúKñ^fNf£þÒ™7z;—èS þä½Ü8;þcÝúߨ¸®×ý†ÿ8÷ù~f¥‰)Gfí×þËð_úf6§ê{©¯Üÿ‘WîVØêÉþvž”iDòNÕ>seþ×ø þù>·sÄVÞ ¿JþþOÎÓ¡ý}¿):ü€ûé.Óò¢Gý—”5ø©8ÿé“ûŽ3ûôÒ”€Züÿ|¨4ΞSf£EfC¯i*döODFd~Cäÿ| 6ÿÒWvÿ>-w—­ÛÏþfåûëÁùóÿòHåe—þ|¨%éEVSþ.òêÌ­üÿû~æ4FPpD'VUêÕfþ?ð2ÿïþ)þÝS™´þ¦ù|rÿùh®ÿüþ’eþGÿ)4Uþ„TCÿ~é3ŽÿÏ·e6éÍ€uS˜{|¤[ÿä÷ÿÉëe7UeDUgU˜eþ|˜“áøoŠ¿Ï*ü¶eUŽ…$€eUS¥ÊUgþ’¯GDä«8€âÍÍr•ý§S…(ˆÉ€6áê%ÇgDÔNÿøV€ògbŒãŒD~RdÔ͉ ¼ìàUŽ»šàÏ0Þó§"õqUbíÄ€EæfÎ ¼ü~?8€_ͺ9CËÇgê~73ä߀Íü7áÛ€ƒãûk€mîý­€8?ï³R­:Šók€>ÿìç€é:?ÿþ~€~dÿ–ÿøS€¤9Ÿé|QþüâJ€º™jê>~ë•öÿ€^(þþñ/e€ÁÿþÆ~/‚$€¸½ÿ~–ÿÿüü€®)šÿðæ?ô þ})ƒ8¯ÿãcôÿô΃$€|‡4"î~3ƒ€>A3@ý"D€>_~éå€c€â?30~¦–€#€^ª}C¯34~úˆ€ì8ªKªƒ#úªó~úȀ爃Lc¯D~úÈ€êƒSƒ4D~úø€ï¿~ì-…ž@ícûú/ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_JP/roms/cbios_main_msx2_jp.rom0000644000175000017500000010000012262345041026114 0ustar manuelmanuel00000000000000óà ¿˜˜Ã’ÃÒ#äÃ$ÃÀÃG$ÃÆÆ$ÃÌÃÞÚÃóÃýÃÃ"Ã.ÃQÃWÃ_ÃqÃ…äÃÃÃâÃëÃÃ5ÔÃæïøÃÃ?ÃrÃÈÃÛÃåÃðÃîÃÃÃÃ!Ã4ÃYÃ÷ÃÅÃêÃÃÿÃÙÃõÃà Ã)ÃÃÃ/ÃAÃSÃCöÃûà ÃfÃyËÃðÃÃÃÕÃÃ-ÃPÃbÃsÂÃ’ãõÃÅà à à Ã2 ÃC ÃS Ãe Ãw É Ú Ã[ÃÃÃ!Ã$Ã'Ã6Ã:Ã>ÃgÃyÃ}ÓÃ&ÃïÃõÃÞÃ,Ã=Ã2ÃGÃQÃÓO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ_Û˜ÉõÍqñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍqÍQ xAO ñÓ˜ü ùÉ:¯üþ0Í_ÍGåë xA<˜í²= ûáÉë:¯üþ0ÍqÍQë xA<˜í³= ûëÉÝåÝ!ÑÃï:¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍqñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍ_:¯üþ8*(ù%%:éóæÍ2*&ù¯Í2É:¯üþ8ÙÑ*(ùÍQ:éóW ó{Ó˜>Ó˜yÓ˜ Íå0 zÓ˜èûÉÝåÝ!ÕÃïÝåÝ!ÙÃïÝåÝ!ÝÃïÝåÝ!áÃï:°óþ)0(:ßóæñGÍ.:àóæçöG Í.³ó¯Í£ ¯Í£É:ßóæñöGÍ.:àóæçöG Í."ù>Í£ ¯Í£É:ßóæñGÍ.:àóæçG Í.½ó¯Í£¯Í£¯Í£¯Í£¯Í£É:ßóæñöGÍ.:àóæçG Í.Çó¯Í£>Í£>Í£¯Í£¯Í£É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í£¯Í£¯Í£¯Í£¯Í£ÉÕõ!Á Fë~#fo)üGñ°GÍ.Ñ É  &o)))Íå0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ(+þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÝåÝ!‰ÃïÉñåÕÅõÍ.í[¹üíK·üÍÅ:éó2òó**ùíKËó @ü:¹üæO Ítð :¹ü/æOÍt*·ü "·üñÁÑáÉ:·üæõÅÕåÍý:·üæ(:,ù/2,ùá ÑÁñÍý:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍQõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°ÍWÝñæð°ÍWÚAŽÓ™á}Ó™ÉÍGÛ˜ÉõÍQñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍZ>!Í…>õ!  Í…õÍ.!¿ÍÃÉÍ.;Í.Í.É!¿í[$ùÃÃÀ:¯üþ Ðå!9ÍáÉKZvŒv °ÃÓ:°óþ(À8€*"ù> Í…>!²ûw³ûí°Ãê¯*$ùoÅÍ…Á:êó*ÉóÃ…:êóæG°*$ùÃ…:êóæG°!):êóæG°°!:êóæG°!:êó!õÍ%ñ,Í(Í!Ô*Í!$Í:öúg.&Í>-Í>À.ÍÍ%É}Í |óÓ™yö€Ó™ûÉ>Í›8øÉíK ùo&))) @üÅÕå:ùÍÒ#áÑÁ#ïÉåõ![Í« ñáÉRIGHTCåõ!mÍ« ñáÉLEFTCåõ!~Í« ñáÉUPCåõ!Í« ñáÉTUPCåõ!Í« ñáÉDOWNCåõ!®Í« ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ! Í« ñáÉSTORECåõ!+ Í« ñáÉSETATRåõ!= Í« ñáÉREADCåõ!N Í« ñáÉSETCåõ!^ Í« ñáÉNSETCXåõ!p Í« ñáÉGTASPCåõ!‚ Í« ñáÉPNTINIåõ!” Í« ñáÉSCANRåõ!¥ Í« ñáÉSCANL>#Ó.Í· >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ¿ ñÍ¿ É|ÍÏ }ÍÏ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Õ%ÃNxÓ¨y2ÿÿÙ!óùÍSÍ—ÍÞͯûÍóO!€ÅåÕ:ÁüÍÒ#áÑÁ¾ ë#ìÝ!€ý*ÀüÍG$>2êó2ëóÍ!i%Í3ó>Ó™>ŽÓ™¯Ó™ö@Ó™>vÓ˜¯Ó™!üúÓ™Û˜þv(ËÎËÖ¯Ó™>ŽÓ™ûxÍ/Í¿#>2ëó>2êó>2éó>2¯óÍÝ!AÍõ!i%Í3Í^ÍÚþÍ=>2™ý¯2)ûÍËþ!&Í3ÃC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@Í¥̲!€Í¥̲Ë(ÆËg(äá#<æ ÛÉGÍÒ##õxÍÒ##Wñ_xÉåÍ•!ABÍÆxáÉåõ!%Í3ñõGæÆ0ÍYxËx(>.ÍYxæÆ0ÍY> ÍY> ÍYñá##Í4(OÕÝáõýáÛ™·úòõåÍG$óáñÍ4(ËéÍ4(ËñÍ4(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍ•z³xÁÉ!Éü@~Ë(å!°%Í3á#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°>!çÿwèÿí°>2çÿ>2èÿ!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!N%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!Y"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉ!ÁüÅåy¶Ë Í ÍáÁØ# é¯2øúÉÅÍ ÁØÆöÉO2øú!Í&þC #Í&þDy7È·ÉyÅåÍÒ#áÁÉvýÉ:¯üþ0 ~·ÈÍY#÷~·ÈÝ!‰Íõ#ó :¯ü·À(:°óþ)ØPÉÅõÍP:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!Í« ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!×Í« ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃG$>ÍÃîåõ!Í« ñáÉINIFNKåõ!Í« ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõ͸ñõÍÍÍ:Ýó2aöñÁÑáÉÍÅÐ(õ:§ü·Âbñþ 8*þʪÍbÍW*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍÃÓ !<Ã> Í—:Ýóæþ òÉ*Üó:±ó,½0 åÍêÍ5á-"ÜóÉ!"ÜóÉÍÓ*ÜóåÍ:Üó!±ó¾0ÍÓÍÓïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!`Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍ!±û:Üó_w:°ó ÍbÃ…Í*ÜóåE:±ó2ÜóG:ÜóÍbë=2ÜóÍbÍsðá"Üó&:±óW•=O!±ûëbk+í¸ÃÓÍ*ÜóåE:±óG:ÜóÍbë<2ÜóÍbÍsðÍÓá"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOþ)8 (Í:°óÖ(OÍÁñÉåÕÅüͤÁÑÕÅ!üÍÃÁá ëá É:ÝóþÈ> Í—Ã7:©üþÀ:¯üþÐ:ÌûÍbÃW:©üþÀ:¯üþÐÍbÍQ2Ìû§_ËËËËË˯!¯ü¾ *·ó*Áóåüͤ:ªüþ !ü!ü~/w#úáøë!üÍÃÍb>ÿÃW 7 Ä Ó ê * 7LUj*E*KÓJñlÐLñM5Y±ALBUCºDÇHêx©y­ͶÿõÍÙ8Í·(öñ > ͪ¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§Ê>2ÝóÃ? Íàý!üÍ3*Ýó"Êû°û&}wÍ4þʘþ 0!™Í¯2¨ü2ªüãõ:¨ü§Ä?ñßÉ͸*Üó"¼ö> 2§öÍbÍQþ (*Üó:°ó¼ &±û~·(:§öÍbÍW*¼ö"ÜóÉÃÍ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉŠ‹Ž‘’•–—ÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!Í« ñáÉISCNTCÃõåõ!Í« ñáÉBEEP"ÜóÉåõ!)Í« ñáÉFNKSBåõ!:Í« ñáÉERAFNKåõ!LÍ« ñáÉDSPFNK:¯üþØ:°üͽý·Ê”Ãåõ!rÍ« ñá7ÉTAPIONåõ!…Í« ñá7ÉTAPINåõ!–Í« ñáÉTAPIOFåõ!©Í« ñá7ÉTAPOONåõ!¼Í« ñá7ÉTAPOUTåõ!ÎÍ« ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>Í<Í<Í<¸>ÍÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Í'/æå!¦_~áÁ§ÉåÕ=(Ëó>óÍû濳_>Í>óÍû/æ!–O ~ÑáÁ§É>Á§Éþ0· >Í'öþ<Éó=ÕGæ(L>Í濳_>Íxæ( >óÍûÑ (>ÿɯÉåõ!Í« ñá¯ÉGTPADåõ!Í« ñáÉGTPDLÍNF#~##¦o&ÉÍN~2öó¯Í¶/æ2èóÍ*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍ>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!ž&!Î&ÍJ!(ݾÄj/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(ÃÉyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍÖÑôÑÁá ~§(åÍÖáñÃ`*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍõÝáÉÙõÅÕåíWõÙýå:øúõýáÍG$ýáÙñâûáÑÁñÙÉþÍÚþ&ÃNåõ!6Í« ñáç%ÃNCALBASí[ÜóÕ"ÜóÍÓÑíSÜóÉÛ™!F( ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!Î%~Ó˜#~§ ùÓ˜§ ùÃ|D|D|DŒ ¤¤ 0H„ø8T’|8T’8Tº8Tþ|þ|DD|DD|@||Püü¤¤ü„„„î¤þ¬ä (D‚|$DH$ÎUìbÒD| |D|D|pþü@@|D„ˆ|((HHŒÿÿðÿÿððB$$B|(H„|T|TT”0ll$$~$~$$>TT>>"T(%B8DD(2J|`` T88T|`` ~`` @8LTTd8088D8@|x8xDDD||@xx8@xDD8|D8D8DD88DD<8 @ ||  8DüÍ $_EñæÍ $GÛ¨W£°áÍ€ó{ÑõËzÄ-%ñáÁÉÈüÉåWÕ§óüñ$ÁáÕå|æoG>üÍ $_EzæÍ $GÛ¨W£°áYÍ…óÑåËzÄ-%áÉÙóýåñÝåáW§üñ$ÕzæGüÝåñæ‡(ËË= ù!z$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ-%ÙÉóåoæG>«ÆUòŽ$W|ægG>Àòž$_/Oz£G}§òé$æåÅG>«ÆUò¸$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: JP Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-^\@[;:],./_abcdefghijklmnopqrstuvwxyz0!"#$%&'()=~|`{+*}<>?_ABCDEFGHIJKLMNOPQRSTUVWXYZ à „‚…€ƒ    à „‚…€ƒ   üçì‘“”•ôõöîí°Þßú™ñèùòû᚟œ’ê—˜æïéøóð÷žàä–åëã›ýâÜÇ̱³´µÔÕÖÎͰÞßÚ¹ÑÈÙÒÛÁº¿¼²Ê·¸ÆÏÉØÓÐ×¾À½Ä¶ÅËû݆‡‰Š‹ŒŽ¢ƒ„…ˆ¦§©ª«¬­®¢£¤¡¥¨¯  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í« > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í« ñáÉBASIC statements are not implemented yetÍ3Éåõ!"}Í« ñáÉunknown@7D179}Ý!‰ÍõÉÝ!…Ãõåõ!~Í« ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_JP/hardwareconfig.xml0000644000175000017500000000502012262345041024363 0ustar manuelmanuel00000000000000 C-BIOS MSX2 JP 2010 An MSX2 machine using C-BIOS, with a Japanese keyboard layout and 60Hz interrupt frequency. MSX2 largest 7d704a6116d5478661eb8743e44d148aa036d721 roms/cbios_main_msx2_jp.rom 2fcb40413e7d373f0f2dbdc815ce18746ddf3684 roms/cbios_sub.rom 512 16000 false jp_ansi true true false V9938 128 YM2149 50on 21000 cbios-msx2_jp.cmos openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_JP/0000755000175000017500000000000012262345041020660 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_JP/roms/0000755000175000017500000000000012262345041021640 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_JP/roms/cbios_logo_msx1.rom0000644000175000017500000004000012262345041025440 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÍo>2êó2ëóÍb*"ù>ÍVÍGŸÍGÍG:éóæG:êóæ°*ÉóÍV*$ù ë0!¼€Í\*$ù ë* Í\*Éó ë0!ìƒÍ\*Éó>ñÍV*"ù„ ë!‡ ÅåÕÍ\á ëá ÁéÉþøþøþ?øþðþüøð?þüøðøøþ?øüþ?ðþþ?þþü?øøøøðððð????øøøøøøðððøøððððððð?Çßøðøøüþ?þþþþüüü|ððððüø?þðüðø?ðþøð|üø?????????ðøøøøøø|<<<ÇÇÇÇããÇÇÇÇüøðð???üþðøøøøøøøøøøøøþ?????~~~~øøøð?~ÇÇÇÇððððÇÇÇÇxxx|??????ðþüüøðøøøøððøþðøü|||||????~~~~þüþðþüøð<ððð🟟Ÿ|~~ã>??øüþ?ð>?üþ?þøñ<ð?üþ|üøø???ððøü?ðøøüþüþ?ðøþ?øþ?þø?ð?þüøð?üþøþ?þþøð ‘‘‘ ‘‘‘‘ ‘ ‘ ‘‘‘‘‘‘ ‘ ‘‘‘‘ ‘‘‘ññññ   ‘‘‘ñññññññññññññññññññññññññññññññ€€€€   ñññññññññññññññññññññññññññññññññññññññ  ññáññááññááñññáñññáññññáññáñññáññááñ€€€€€ááááááááááááááááááááááááááááááááááááááက€€€€€€€„€€‚ƒ„…†‡€€€€€€€€€€€€€€€€ˆ‰Š‹ŒŽŒŒŒŒŒŒŒŒŒŒŒŒŒã‘’“”Œ•–ŒŒŒŒ—ŒŒŒŒŒŒŒŒŒŒŒä˜™šŒŒ›œžŸ ¡¢£¤¥¦Œä§¨©ŒŒª«ŒŒ¬­®¯ŒŒ°±²³³³´ŒäµµŒŒŒ¶ŒŒŒ·¸¹ºŒŒ»¼½¾¾¿ÀŒäÁÂÃŒŒÄÅÅÆÇÈÉÊËÌÍÎÅÅÅÏÐŒäÑÂÒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒäÓÔÕÖרÙÚÛŒŒŒŒŒŒŒŒŒV0.25ä€ÜÝÞÂßàáâââââââââââââââåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_JP/roms/cbios_main_msx1_jp.rom0000644000175000017500000010000012262345041026112 0ustar manuelmanuel00000000000000óà ¿˜˜Ãíÿ#ÃÿÃ$ÃÃ4$Ã!Ãs$Ã'Ã9ÃæÃNÃXÃÃ"Ã.ÃEÃKÃSÃ^ÃkÃ~ÑäÃËÃ^ÃÃtôÃ÷Ã5ÃéÃØà ÃaÃtÃ~ÉÃ:ÃRÃ\ÃjÃ|ÃôÃÙÃÃÃ6ÃTÃKÃ%ÃAÃSÃVçÃfÃjÃ{ÃßÃÃÃGÃYòÃÅÃ×ÃéÃüÃÃ!ÃjÃyÃ6ÃHÃYÃhÃxÉÛëÃæÃôà à Ã) Ã9 ÃK Ã] Ão À çÃaÃjÃmÃpÃsÂÆÊóÃÅÃÉÃßÃrÉO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóx wáÉÍSÛ˜ÉõÍ^ñÓ˜Éó}Ó™|æ?Ó™ûÉó}Ó™|æ?ö@Ó™ûÉõÍ^ xAO ñÓ˜ü ùÉÍSåë xA<˜í²= ûáÉëÍ^ë xA<˜í³= ûëÉþÐ!­Ãt´÷5ó!ßó™€í£xíQ· ÷ûÃ":¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍ^ñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍ?:¯üþ8*(ù%%:éóæÍÂ*&ù¯ÍkÉ:¯üþ8ÙÑ*(ùÍ^:éóW ó{Ó˜>Ó˜yÓ˜ Í~0 zÓ˜èûÉÍ>2¯ü2°ü:®ó2°ó>2Üó2Ýó*³ó""ù*·ó"$ù*¹ó"(ù*»ó"&ùÍËÍ͚ͿõÍ>2¯ü2°ü>2Üó2ÝóÍË*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍš:¯ó2°óÍ©ÍJÍÎõÍ>2¯üÍË*Çó""ùÍ^¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ùÍØÍJÍêõÍ>2¯üÍË*Ñó""ùÍ^¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ùÍ ÍJÍõ:ßóæñGÍ.:àóæçöG Í.³ó¯Í< ¯Í<É:ßóæñGÍ.:àóæçG Í.½ó¯Í<¯Í<¯Í<¯Í<¯Í<É:ßóæñöGÍ.:àóæçG Í.Çó¯Í<>Í<>Í<¯Í<¯Í<É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í<¯Í<¯Í<¯Í<¯Í<ÉÕõ!Z Fë~#fo)üGñ°GÍ.Ñ É  &o)))Í~0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍí[¹üíK·üÍ«:éó2òó**ùíKËó @ü:¹üæO Íð :¹ü/æOÍ*·ü "·üñÁÑáÉ:·üæõÅÕåÍ:·üæ(:,ù/2,ùá ÑÁñÍ:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍEõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°ÍKÝñæð°ÍKÚAŽÓ™á}Ó™ÉÍ×Û˜ÉõÍáñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍÎ>!Ík>õ!  ÍkõÍ.!¿Í‘ÉÉ!¿í[$ùÑÀ:¯üþÐå!·ÍáÉ¿Îê:°óþ(À8€*"ù> Ík>!²ûw³ûí°ÃE¯*$ùoÅÍkÁ:êó*ÉóÃk:êóæG°*$ùÃkíK ùo&))) @üÅÕå:ùÍ¿#áÑÁ#ïÉåõ!AÍ‘ ñáÉRIGHTCåõ!SÍ‘ ñáÉLEFTCåõ!dÍ‘ ñáÉUPCåõ!sÍ‘ ñáÉTUPCåõ!ƒÍ‘ ñáÉDOWNCåõ!”Í‘ ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!ÿÍ‘ ñáÉSTORECåõ! Í‘ ñáÉSETATRåõ!# Í‘ ñáÉREADCåõ!4 Í‘ ñáÉSETCåõ!D Í‘ ñáÉNSETCXåõ!V Í‘ ñáÉGTASPCåõ!h Í‘ ñáÉPNTINIåõ!z Í‘ ñáÉSCANRåõ!‹ Í‘ ñáÉSCANL>#Ó.Í >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ¥ ñÍ¥ É|͵ }͵ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Â%ÉxÓ¨y2ÿÿÙ!óùÍÍCÍ?ûÍN!€ÅåÕ:ÁüÍ¿#áÑÁ¾ ë#ìÝ!€ý*ÀüÍ4$>2êó2ëóÍ´!V%ÍŽûxÍŠ>2ëó>2êó>2éó>2¯óÍ´!V%ÍŽÍ"ÍÚþÍ>2™ý¯2)ûÍËþ!ü%ÍŽÃgC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@ÍiÌv!€ÍiÌvË(ÆËg(äá#<æ ÛÉGÍ¿##õxÍ¿##Wñ_xÉåÍY!ABÍ!xáÉåõ!Š%ÍŽñõGæÆ0Í´xËx(>.Í´xæÆ0Í´> Í´> Í´ñá##Íø(OÕÝáõýáÛ™·ú¶õåÍ4$óáñÍø(ËéÍø(ËñÍø(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍYz³xÁÉ!Éü@~Ë(å!%ÍŽá#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!;%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!´"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉvýÉ:¯üþ0 ~·ÈÍ´#÷~·ÈÝ!‰ÍA#ó :¯ü·À(:°óþ)ØPÉÅõÍ«:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!øÍ‘ ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!2Í‘ ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃ4$>ÍRÃ:åõ!cÍ‘ ñáÉINIFNKåõ!uÍ‘ ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõÍñõÍÚÍ:Ýó2aöñÁÑáÉÍÐ(õ:§ü·Â½ñþ 8*þÊöͽÍK*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍlÃ. !ˆÃ> Íò:Ýóæþ òÉ*Üó:±ó,½0 åÍEÍá-"ÜóÉ!"ÜóÉÍ.*ÜóåÍl:Üó!±ó¾0Í.Í.ïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!¬Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍl!±û:Üó_w:°ó ͽÃkÍl*ÜóåE:±ó2ÜóG:Üóͽë=2ÜóͽÍÎðá"Üó&:±óW•=O!±ûëbk+í¸Ã.Íl*ÜóåE:±óG:Üóͽë<2ÜóͽÍÎðÍ.á"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOÍÜÁñÉåÕÅüÍ~ÁÑÕÅ!üÍ‘Áá ëá É:ÝóþÈ> ÍòÃ’:©üþÀ:¯üþÐ:ÌûͽÃK:©üþÀ:¯üþÐͽÍE2Ìû§_ËËËËË˯!¯ü¾ *·ó*ÁóåüÍ~:ªüþ !ü!ü~/w#úáøë!üÍ‘ͽ>ÿÃKV’  . E ¨ lrx’§°j¨E¨K.JLl+LLMY A§B°CD"HExyͶÿõÍ%8Í(öñ > Íö¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§ÊT>2ÝóÃT? Íàý!HÍŽ*Ýó"Êû°û&}wÍþÊäþ 0!åͯ2¨ü2ªüãõ:¨ü§Ä‹ñßÉÍ*Üó"¼ö> 2§öͽÍEþ (*Üó:°ó¼ &±û~·(:§öͽÍK*¼ö"ÜóÉÃ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉÖ×ÚÛÜÝÞáâãÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!LÍ‘ ñáÉISCNTCÃAåõ!aÍ‘ ñáÉBEEP"ÜóÉåõ!uÍ‘ ñáÉFNKSBåõ!†Í‘ ñáÉERAFNKåõ!˜Í‘ ñáÉDSPFNK:¯üþØ:°üͽý·Êtôåõ!¾Í‘ ñá7ÉTAPIONåõ!ÑÍ‘ ñá7ÉTAPINåõ!âÍ‘ ñáÉTAPIOFåõ!õÍ‘ ñá7ÉTAPOONåõ!Í‘ ñá7ÉTAPOUTåõ!Í‘ ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>ÍR<ÍR<ÍR<¸>ÍRÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Ís/æå!ò_~áÁ§ÉåÕ=(Ëó>óÍ\û濳_>ÍR>óÍ\û/æ!âO ~ÑáÁ§É>Á§Éþ0· >Ísöþ<Éó=ÕGæ(L>Í\濳_>ÍRxæ( >óÍ\ûÑ (>ÿɯÉåõ!SÍ‘ ñá¯ÉGTPADåõ!dÍ‘ ñáÉGTPDLÍšF#~##¦o&ÉÍš~2öó¯Í/æ2èóÍc*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍc>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!‹&!»&Í–! (ݾĶ/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(Ãyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍ"ÑôÑÁá ~§(åÍ"áñì*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍAÝáÉÙõÅÕåíWõÙýå:øúõýáÍ4$ýáÙñâ`ûáÑÁñÙÉþÍÚþï%Éåõ!‚Í‘ ñáÔ%ÉCALBASÛ™!3( ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!»%~Ó˜#~§ ùÓ˜§ ùÃg|D|D|DŒ ¤¤ 0H„ø8T’|8T’8Tº8Tþ|þ|DD|DD|@||Püü¤¤ü„„„î¤þ¬ä (D‚|$DH$ÎUìbÒD| |D|D|pþü@@|D„ˆ|((HHŒÿÿðÿÿððB$$B|(H„|T|TT”0ll$$~$~$$>TT>>"T(%B8DD(2J|`` T88T|`` ~`` @8LTTd8088D8@|x8xDDD||@xx8@xDD8|D8D8DD88DD<8 @ ||  8DüÍø#_EñæÍø#GÛ¨W£°áÍ€ó{ÑõËzÄ%ñáÁÉÈüÉåWÕ§óüÞ$ÁáÕå|æoG>üÍø#_EzæÍø#GÛ¨W£°áYÍ…óÑåËzÄ%áÉÙóýåñÝåáW§üÞ$ÕzæGüÝåñæ‡(ËË= ù!g$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ%ÙÉóåoæG>«ÆUò{$W|ægG>Àò‹$_/Oz£G}§òÖ$æåÅG>«ÆUò¥$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: JP Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-^\@[;:],./_abcdefghijklmnopqrstuvwxyz0!"#$%&'()=~|`{+*}<>?_ABCDEFGHIJKLMNOPQRSTUVWXYZ à „‚…€ƒ    à „‚…€ƒ   üçì‘“”•ôõöîí°Þßú™ñèùòû᚟œ’ê—˜æïéøóð÷žàä–åëã›ýâÜÇ̱³´µÔÕÖÎͰÞßÚ¹ÑÈÙÒÛÁº¿¼²Ê·¸ÆÏÉØÓÐ×¾À½Ä¶ÅËû݆‡‰Š‹ŒŽ¢ƒ„…ˆ¦§©ª«¬­®¢£¤¡¥¨¯  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í‘ > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í‘ ñáÉBASIC statements are not implemented yetÍŽÉåõ!"}Í‘ ñáÉunknown@7D179}Ý!‰ÍAÉÝ!…ÃAåõ!~Í‘ ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_JP/hardwareconfig.xml0000644000175000017500000000365512262345041024376 0ustar manuelmanuel00000000000000 C-BIOS MSX1 JP 2010 An MSX1 machine using C-BIOS, with an Japanese keyboard layout and 60Hz interrupt frequency. MSX 9921987867eb11657f8fb86b45304f4351a6ff32 roms/cbios_main_msx1_jp.rom 16000 false jp_ansi false true false TMS99X8A 16 50on YM2149 21000 openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2/0000755000175000017500000000000012262345041020270 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2/roms/0000755000175000017500000000000012262345041021250 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2/roms/cbios_sub.rom0000644000175000017500000004000012262345041023731 0ustar manuelmanuel00000000000000CD~·ÉÉÉÉ|ºÀ}»ÉÉÉÉÉÉÉÉÉÉõÅÕåÙõÅÕåÝåýåÝ!8ý*ÀüÍÅýáÝááÑÁñÙáÑÁñíMÉÉÉÉÉÍÖýíEÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃòûÃûÃü ûÃsûÃyûÃgûÊûÃ…û× û×ûé ûÃê ûÃÙ ûÃÉ ûú ûÃÐûÃáûéûà ûóûÃÅûÃûÃ`ûÃ¥ûÃøûÃPûÃûòûÃfûÃûÃûÃ%ûÃu ûÃÊ ûÃÄ ûÃûÃn ûÃMÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃÿûÃÒ ÉÉÉÉÉÉÉÉûÃaûüûÃØûÃïûÃÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃ|ûÃŒûÞûðÉÉÉÉûÃûÃûÃ’ûÃòûÃûÃûÃ%ûÃ6ÉÉÉÉûÃHÉÉÉÉûÃOÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃaûÃ~ÉÉÉO þ#(##øÉN#fié>#Ó.Í">Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ*ñÍ*É|Í:}Í:ÉÅåõW§óüoñáåÕåõ|æoG>ü͉_Eñæ͉GÛ¨W£°áÍ€ó{ÑõËzÄ«ñáÁÉÈüÉåWÕ§óüoÁáÕå|æoG>ü͉_Ezæ͉GÛ¨W£°áYÍ…óÑåËzÄ«áÉÙóýåñÝåáW§üoÕzæGüÝåñæ‡(ËË= ù!øåÛ¨õ¡°ÙÃŒóÙóÑËzÄ«ÙÉóåoæG>«ÆUò W|ægG>Àò_/Oz£G}§ògæåÅG>«ÆUò6£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝé:àóæ¿GÍÿÉ:àóö@GÍÿÉó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ0Û˜ÉõÍBñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍB͈ xAO ñÓ˜ü ùÉ:¯üþ0Í0Í~ åë xA<˜í²= ûáÉë:¯üþ0ÍB͈ ë xA<˜í³= ûëÉþ Ð!¼ÃÅ`¥s ý [ ·  !èÿ:¯üþ0˾Ëþ¯2ìÿ¯2öÿó!ßó™€í£xíQ· ÷>Ó™>‘Ó™!çÿ›í³###>Ó™>‘Ó™›í³ûÃó:¯üþ(:=õ:éóæðo:ëóµGÍÿñÀ:éóæð!êó¶*¿ó õÍBñÓ˜õ x± ÷ñÉ:ëóGÃÿ:¯ü·ÈÍ:¯üþ8*(ù%%:éóæÍi *&ù¯Íi É:¯üþ8ÙÑ*(ù͈ :éóW ó{Ó˜>Ó˜yÓ˜ Í%0 zÓ˜èûÉÍç>2¯ü2°ü¯2õú2öú:®ó2°ó>2Üó2Ýó*³ó""ù*·ó:°óþ)8!"$ù*¹ó"(ù*»ó"&ùÍÍøÍ\ Í ÃÎÍç>2¯ü2°ü>2Üó2ÝóÍ*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍ\ :¯ó2°ó¯2õú2öúÍP͛͞ ÃÎÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù¯2õú2öúÍÍ›ͽ ÃÎÍç>2¯üÍ*Ñó""ùÍB¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ù¯2õú2öúͲÍ›ÍÓ ÃÎ:°óþ)0(:ßóæñGÍÿ:àóæçöG Íÿ³ó¯Íã ¯ÍãÉ:ßóæñöGÍÿ:àóæçöG Íÿ"ù>Íã ¯ÍãÉ:ßóæñGÍÿ:àóæçG Íÿ½ó¯Íã¯Íã¯Íã¯Íã¯ÍãÉ:ßóæñöGÍÿ:àóæçG ÍÿÇó¯Íã>Íã>Íã¯Íã¯ÍãÉ:ßóæñGÍÿ:àóæçöG ÍÿÑó¯Íã¯Íã¯Íã¯Íã¯ÍãÉÕõ! Fë~#fo)üGñ°GÍÿÑ É  &o)))Í%0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍu í[¹üíK·üÍ :éó2òó**ùíKËó @ü:¹üæO Í«ð :¹ü/æOÍ«*·ü "·üñÁÑáÉ:·üæõÅÕåÍ4 :·üæ(:,ù/2,ùá ÑÁñÍ4 :,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍ"õæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í(Ýñæð°Í(ÚAŽÓ™á}Ó™ÉÍ~ Û˜Éõ͈ ñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™ÍÿàÍÿÍÿ€ÍÿÍÿÍÿ>2Üó2ÝóÍž >!ÍV>õ!  ÍVõÍÿ!¿ý*ÀüÝ!\ÍÅûÉÍÿ;ÍÿÍÿÉ!¿í[$ùý*ÀüÝ!\ÍÅûÉÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù:ßóæñöGÍÿ:àóæçG ÍÿÍÿÇó¯Íã>Íã>Íãû >ÍãÏó¯Íã!éÿ6#6Í›Íÿͽ ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçö G Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Íç ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçG Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Í÷ ÃÎÍç>2¯üÍ:ßóæñö GÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÍç>2¯üÍ:ßóöGÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÀ:¯üþ Ðå!} ÍáÉ ž ½ Ó ½ ç ÷  :°óþ(À8€*"ù> ÍV!"Üó}!²ûw³ûí°É¯*$ùoÅÍVÁ:êó*ÉóÃV:êóæG°*$ùÃV:êóæG°!):êóæG°°!:êóæG°!:êó!õÍl ñ,Íb (Í\ !Ô*Í\ !$Í\ :öúg.&Í\ >-Íb >À.Íb Íl É}Íb |óÓ™yö€Ó™ûÉ>ÍÒ 8øÉíK ùo&))) @üÅÕå:ùÍPáÑÁ#ïÉåõ!¢ ÍñáÉRIGHTCåõ!´ ÍñáÉLEFTCåõ!Å ÍñáÉUPCåõ!Ô ÍñáÉTUPCåõ!ä ÍñáÉDOWNCåõ!õ ÍñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!`ÍñáÉSTORECåõ!rÍñáÉSETATRåõ!„ÍñáÉREADCåõ!•ÍñáÉSETCåõ!¥ÍñáÉNSETCXåõ!·ÍñáÉGTASPCåõ!ÉÍñáÉPNTINIåõ!ÛÍñáÉSCANRåõ!ìÍñáÉSCANLåõ!ýÍñáÉDOGRPHÅÕåÍu :éó2òó*·ü"fõ "·ü:öú*¹üg"hõíCjõíClõ!@üË:òó8:êó2nõÍÂ:ûæö°íy>¬Ó™>‘Ó™û!@üË:òó8:êóÓ› ñ#ìáÑÁÉåõ!~ÍñáÉMAPXYCåõ!ÍñáÉTRIGHTåõ!¢ÍñáÉTRIGHT:öúWÕíCfõíShõ*³ü§íB"jõ!"lõ¯2oõ:òó2nõÍý*µü:öúg"hõÍýÑíShõ>2oõ:µü“2jõÍý*³ü"fõÍýÉÍÂ:ûæöpíyûÉ*µüç0ë§íR#"lõ:öúWíShõÅÑ*³üç0ë§íR#"jõíSfõ¯2oõ:òó2nõÍÂ:ûæö€íyûÉåõ!ZÍñáÃn CLRTXTåÕÅ*"ùËË:õú„öGÍÿ:¯üþ :åóæG:õú°GÍÿ:õúO:¯üþ Ë!¯*(ù)±o>´GÍÿE ÍÿÁÑáÉåÍ'͈ Íÿ !\~Ó˜#ÓšøáÉåÍ'Í~ áÍÿ Û˜ÓšúÉåõÍ'ñ‡O Í~ Û˜GÛ˜OáÉõÅåÍ'z‡O ͈ áBÍÿÁñÓšÓ˜{ÓšÓ˜É:¯ü·Ì>ÍÒ 8øó> Ó™>‘Ó™›!bõí³Éx±7Èz³7Èå!:¯üæþ:¯ü $·íBáØå!Ô·íRáÉíKjõí[lõÍÜØÍÂ~æöíyû·É*bõN#F#íCjõ^#V#íSlõÍÜØþ( þ(N¯CËËø2nõBÅåÍÂ~æö°íy>¬Ó™>‘Ó™ûáÁ>ÍÒ ËGÈË(ôx§(ůCËËøÓ›yÁOî#NBÙ*fõíKjõí[lõq#p#s#r#ÍÜØþ( þ(åÍÂ~æ> íyûáB>ÍÒ Ë ËG óÅ>ÍÒ CËü±ÁOãq#>ÍÒ 8ÖÉåõ!ýÍñáÉBLTVDåõ!ÍñáÉBLTDVåõ!ÍñáÉBLTMDåõ!0ÍñáÉBLTDMåõ!AÍñáÉNEWPADͳͼÉåõ!ZÍñáÉKNJPRTÅ> Ó´yæGÛµ°ÁÓµyæÓ´ÛµæÉÅGÅ> Ó´yæGÛµ°ÁÓµyæÓ´xæÓµÁÉopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2/roms/cbios_logo_msx2.rom0000644000175000017500000004000012262345041025051 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÀ!HƒÍ΂!"êó>Í_!vÍ;ÍA!"êóÍb:èÿæG ÍG>Ý!1Í_ËG óÕ!gí°á Àwëó> Ó™>‘Ó™å›!gí³á>¬Ó™>‘Ó™ûÿí³>Ý!1Í_ËG(í³ï À!ví°!°"·ü!a"¹ü>2éó>2û!È‚ÍJ2ûÍD–!ÀæðO~æð¹(0ÆÖO~æ±wæO~æ¹(0<=O~æð±w#O~¹(0<=w#¿!ÀÍ;vý –!À¾  #ø !¶‚^#V#ÅåëÍ;áÁvvðÉó¯Ó™>Ó™š í³ûÉ:¯üþ0 ~·ÈÍ¢#÷~·ÈÝ!‰Í_#óUð''''''''''''''''3Uwtcsrrrpp'3Uwtcsrrrpw'3Uwtcsrrrwp'3Uwtcsrrwpp'3Uwtcsrwrpp'3Uwtcswrrpp'3Uwtcwrrrpp'3Uwwcsrrrpp'3Uwtwsrrrpp¶Öö‚6‚V‚v‚–‚–V0.25Ù€ÙÙÍ$ƒÙ8í õ~#·(0Ù_P0Í$ƒËÍ$ƒËÍ$ƒËÍ$ƒ8Ë»Í1ƒåÙåÕÙbkÁíBÁí°áÀÍ0ƒØåÙåÙÁbk+í°á¯Ë!ÀÙ~#ÙOË! ÉÙ!Í$ƒ0øÍ$ƒíjù#ÙÉ~3Á@ý¢Cy6©™4}cû:™šj»º…$“ƒ¿a~ªª»ª©€?®Ç~«~Œƒ¬€Y~ï€×g~}¬€[~âªÙ‰j€+…~˜CèrºªµýW‰ˆcº“PzùüïföÙ¶÷a 8˜š€Mû–9¿ö<‘ˆ€Ò©Ž7õ_ ”™ö~€ž&ŽþŒ9›á¹l戗€ßü¤™ìóë‚8Š»€_wòcþ¸êõ–€f““Aþ€®Š1ïüŒþ|³‰‰øŸ“Ï™ÌWéìþé9œ~ÁÉ“ý´þ|+fNá|6Á¹œþ!WwvNð¾5?à¥þâ|Vgå<Ì(3ÿó|fGUVòdCÌþüéwcÿý93þ?“µ:î3kþ/õ”ýÕý¹õïyÄŠw çΰ:z?c!8óÎDw{gKW”Q—\#vǯÈ'C>þ§ã·R•gç~xfÇ€Y+#yþSÝ–ÙézÈYgeUÜn8vcç9FV¡#f‘Œ+zUþ8Cø’VÜçgsfþE˜ÞHg™åf‡-ÓÿèÝýÿ=ÉÒfã_of1ë¾'< Û­Ïøi;þø¶ëYðfðŽ‹c™ËØ#C=þ—ÜûúKñ^fNf£þÒ™7z;—èS þä½Ü8;þcÝúߨ¸®×ý†ÿ8÷ù~f¥‰)Gfí×þËð_úf6§ê{©¯Üÿ‘WîVØêÉþvž”iDòNÕ>seþ×ø þù>·sÄVÞ ¿JþþOÎÓ¡ý}¿):ü€ûé.Óò¢Gý—”5ø©8ÿé“ûŽ3ûôÒ”€Züÿ|¨4ΞSf£EfC¯i*döODFd~Cäÿ| 6ÿÒWvÿ>-w—­ÛÏþfåûëÁùóÿòHåe—þ|¨%éEVSþ.òêÌ­üÿû~æ4FPpD'VUêÕfþ?ð2ÿïþ)þÝS™´þ¦ù|rÿùh®ÿüþ’eþGÿ)4Uþ„TCÿ~é3ŽÿÏ·e6éÍ€uS˜{|¤[ÿä÷ÿÉëe7UeDUgU˜eþ|˜“áøoŠ¿Ï*ü¶eUŽ…$€eUS¥ÊUgþ’¯GDä«8€âÍÍr•ý§S…(ˆÉ€6áê%ÇgDÔNÿøV€ògbŒãŒD~RdÔ͉ ¼ìàUŽ»šàÏ0Þó§"õqUbíÄ€EæfÎ ¼ü~?8€_ͺ9CËÇgê~73ä߀Íü7áÛ€ƒãûk€mîý­€8?ï³R­:Šók€>ÿìç€é:?ÿþ~€~dÿ–ÿøS€¤9Ÿé|QþüâJ€º™jê>~ë•öÿ€^(þþñ/e€ÁÿþÆ~/‚$€¸½ÿ~–ÿÿüü€®)šÿðæ?ô þ})ƒ8¯ÿãcôÿô΃$€|‡4"î~3ƒ€>A3@ý"D€>_~éå€c€â?30~¦–€#€^ª}C¯34~úˆ€ì8ªKªƒ#úªó~úȀ爃Lc¯D~úÈ€êƒSƒ4D~úø€ï¿~ì-…ž@ícûú/ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2/roms/cbios_main_msx2.rom0000644000175000017500000010000012262345041025032 0ustar manuelmanuel00000000000000óà ¿˜˜Ã’ÃÒ#äÃ$ÃÀÃG$ÃÆÆ$ÃÌ¡ÃÞÚÃóÃýÃÃ"Ã.ÃQÃWÃ_ÃqÃ…äÃÃÃâÃëÃÃ5ÔÃæïøÃÃ?ÃrÃÈÃÛÃåÃðÃîÃÃÃÃ!Ã4ÃYÃ÷ÃÅÃêÃÃÿÃÙÃõÃà Ã)ÃÃÃ/ÃAÃSÃCöÃûà ÃfÃyËÃðÃÃÃÕÃÃ-ÃPÃbÃsÂÃ’ãõÃÅà à à Ã2 ÃC ÃS Ãe Ãw É Ú Ã[ÃÃÃ!Ã$Ã'Ã6Ã:Ã>ÃgÃyÃ}ÓÃ&ÃïÃõÃÞÃ,Ã=Ã2ÃGÃQÃÓO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ_Û˜ÉõÍqñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍqÍQ xAO ñÓ˜ü ùÉ:¯üþ0Í_ÍGåë xA<˜í²= ûáÉë:¯üþ0ÍqÍQë xA<˜í³= ûëÉÝåÝ!ÑÃï:¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍqñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍ_:¯üþ8*(ù%%:éóæÍ2*&ù¯Í2É:¯üþ8ÙÑ*(ùÍQ:éóW ó{Ó˜>Ó˜yÓ˜ Íå0 zÓ˜èûÉÝåÝ!ÕÃïÝåÝ!ÙÃïÝåÝ!ÝÃïÝåÝ!áÃï:°óþ)0(:ßóæñGÍ.:àóæçöG Í.³ó¯Í£ ¯Í£É:ßóæñöGÍ.:àóæçöG Í."ù>Í£ ¯Í£É:ßóæñGÍ.:àóæçG Í.½ó¯Í£¯Í£¯Í£¯Í£¯Í£É:ßóæñöGÍ.:àóæçG Í.Çó¯Í£>Í£>Í£¯Í£¯Í£É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í£¯Í£¯Í£¯Í£¯Í£ÉÕõ!Á Fë~#fo)üGñ°GÍ.Ñ É  &o)))Íå0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ(+þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÝåÝ!‰ÃïÉñåÕÅõÍ.í[¹üíK·üÍÅ:éó2òó**ùíKËó @ü:¹üæO Ítð :¹ü/æOÍt*·ü "·üñÁÑáÉ:·üæõÅÕåÍý:·üæ(:,ù/2,ùá ÑÁñÍý:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍQõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°ÍWÝñæð°ÍWÚAŽÓ™á}Ó™ÉÍGÛ˜ÉõÍQñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍZ>!Í…>õ!  Í…õÍ.!¿ÍÃÉÍ.;Í.Í.É!¿í[$ùÃÃÀ:¯üþ Ðå!9ÍáÉKZvŒv °ÃÓ:°óþ(À8€*"ù> Í…>!²ûw³ûí°Ãê¯*$ùoÅÍ…Á:êó*ÉóÃ…:êóæG°*$ùÃ…:êóæG°!):êóæG°°!:êóæG°!:êó!õÍ%ñ,Í(Í!Ô*Í!$Í:öúg.&Í>-Í>À.ÍÍ%É}Í |óÓ™yö€Ó™ûÉ>Í›8øÉíK ùo&))) @üÅÕå:ùÍÒ#áÑÁ#ïÉåõ![Í« ñáÉRIGHTCåõ!mÍ« ñáÉLEFTCåõ!~Í« ñáÉUPCåõ!Í« ñáÉTUPCåõ!Í« ñáÉDOWNCåõ!®Í« ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ! Í« ñáÉSTORECåõ!+ Í« ñáÉSETATRåõ!= Í« ñáÉREADCåõ!N Í« ñáÉSETCåõ!^ Í« ñáÉNSETCXåõ!p Í« ñáÉGTASPCåõ!‚ Í« ñáÉPNTINIåõ!” Í« ñáÉSCANRåõ!¥ Í« ñáÉSCANL>#Ó.Í· >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ¿ ñÍ¿ É|ÍÏ }ÍÏ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Ù%ÃNxÓ¨y2ÿÿÙ!óùÍSÍ—ÍÞͯûÍóO!€ÅåÕ:ÁüÍÒ#áÑÁ¾ ë#ìÝ!€ý*ÀüÍG$>2êó2ëóÍ!i%Í3ó>Ó™>ŽÓ™¯Ó™ö@Ó™>vÓ˜¯Ó™!üúÓ™Û˜þv(ËÎËÖ¯Ó™>ŽÓ™ûxÍ/Í¿#>2ëó>2êó>2éó>2¯óÍÝ!AÍõ!i%Í3Í^ÍÚþÍ=>2™ý¯2)ûÍËþ!&Í3ÃC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@Í¥̲!€Í¥̲Ë(ÆËg(äá#<æ ÛÉGÍÒ##õxÍÒ##Wñ_xÉåÍ•!ABÍÆxáÉåõ!¡%Í3ñõGæÆ0ÍYxËx(>.ÍYxæÆ0ÍY> ÍY> ÍYñá##Í4(OÕÝáõýáÛ™·úòõåÍG$óáñÍ4(ËéÍ4(ËñÍ4(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍ•z³xÁÉ!Éü@~Ë(å!´%Í3á#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°>!çÿwèÿí°>2çÿ>2èÿ!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!N%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!Y"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉ!ÁüÅåy¶Ë Í ÍáÁØ# é¯2øúÉÅÍ ÁØÆöÉO2øú!Í&þC #Í&þDy7È·ÉyÅåÍÒ#áÁÉvýÉ:¯üþ0 ~·ÈÍY#÷~·ÈÝ!‰Íõ#ó :¯ü·À(:°óþ)ØPÉÅõÍP:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!Í« ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!×Í« ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃG$>ÍÃîåõ!Í« ñáÉINIFNKåõ!Í« ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõ͸ñõÍÍÍ:Ýó2aöñÁÑáÉÍÅÐ(õ:§ü·Âbñþ 8*þʪÍbÍW*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍÃÓ !<Ã> Í—:Ýóæþ òÉ*Üó:±ó,½0 åÍêÍ5á-"ÜóÉ!"ÜóÉÍÓ*ÜóåÍ:Üó!±ó¾0ÍÓÍÓïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!`Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍ!±û:Üó_w:°ó ÍbÃ…Í*ÜóåE:±ó2ÜóG:ÜóÍbë=2ÜóÍbÍsðá"Üó&:±óW•=O!±ûëbk+í¸ÃÓÍ*ÜóåE:±óG:ÜóÍbë<2ÜóÍbÍsðÍÓá"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOþ)8 (Í:°óÖ(OÍÁñÉåÕÅüͤÁÑÕÅ!üÍÃÁá ëá É:ÝóþÈ> Í—Ã7:©üþÀ:¯üþÐ:ÌûÍbÃW:©üþÀ:¯üþÐÍbÍQ2Ìû§_ËËËËË˯!¯ü¾ *·ó*Áóåüͤ:ªüþ !ü!ü~/w#úáøë!üÍÃÍb>ÿÃW 7 Ä Ó ê * 7LUj*E*KÓJñlÐLñM5Y±ALBUCºDÇHêx©y­ͶÿõÍÙ8Í·(öñ > ͪ¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§Ê>2ÝóÃ? Íàý!üÍ3*Ýó"Êû°û&}wÍ4þʘþ 0!™Í¯2¨ü2ªüãõ:¨ü§Ä?ñßÉ͸*Üó"¼ö> 2§öÍbÍQþ (*Üó:°ó¼ &±û~·(:§öÍbÍW*¼ö"ÜóÉÃÍ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉŠ‹Ž‘’•–—ÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!Í« ñáÉISCNTCÃõåõ!Í« ñáÉBEEP"ÜóÉåõ!)Í« ñáÉFNKSBåõ!:Í« ñáÉERAFNKåõ!LÍ« ñáÉDSPFNK:¯üþØ:°üͽý·Ê”Ãåõ!rÍ« ñá7ÉTAPIONåõ!…Í« ñá7ÉTAPINåõ!–Í« ñáÉTAPIOFåõ!©Í« ñá7ÉTAPOONåõ!¼Í« ñá7ÉTAPOUTåõ!ÎÍ« ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>Í<Í<Í<¸>ÍÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Í'/æå!¦_~áÁ§ÉåÕ=(Ëó>óÍû濳_>Í>óÍû/æ!–O ~ÑáÁ§É>Á§Éþ0· >Í'öþ<Éó=ÕGæ(L>Í濳_>Íxæ( >óÍûÑ (>ÿɯÉåõ!Í« ñá¯ÉGTPADåõ!Í« ñáÉGTPDLÍNF#~##¦o&ÉÍN~2öó¯Í¶/æ2èóÍ*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍ>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!¢&!Ò&ÍJ!Â'ݾÄj/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(ÃÉyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍÖÑôÑÁá ~§(åÍÖáñÃ`*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍõÝáÉÙõÅÕåíWõÙýå:øúõýáÍG$ýáÙñâûáÑÁñÙÉþÍÚþ&ÃNåõ!6Í« ñáë%ÃNCALBASí[ÜóÕ"ÜóÍÓÑíSÜóÉÛ™!ê' ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!Ò%~Ó˜#~§ ùÓ˜§ ùÃüÍ $_EñæÍ $GÛ¨W£°áÍ€ó{ÑõËzÄ-%ñáÁÉÈüÉåWÕ§óüñ$ÁáÕå|æoG>üÍ $_EzæÍ $GÛ¨W£°áYÍ…óÑåËzÄ-%áÉÙóýåñÝåáW§üñ$ÕzæGüÝåñæ‡(ËË= ù!z$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ-%ÙÉóåoæG>«ÆUòŽ$W|ægG>Àòž$_/Oz£G}§òé$æåÅG>«ÆUò¸$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: EU/INT Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-=\[];'`,./abcdefghijklmnopqrstuvwxyz)!@#$%^&*(_+|{}:"~<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ ¬«ºï½ôûìñ »óòļÇÍÜÆÝÈ ÂÛÌÒÀÏ ýüõð÷®¯öþúÁÎÔÖßÊÞÉ ÓÃ×Ë©ÑÅÕÐùªøëŸÙ¿›˜àáç‡îéíÚ·¹å†¦§„—‹Œ”±¡‘³µæ¤¢£ƒ“‰–‚•ˆŠ …Ø­ž¾œâ€è궸䨎™š°’²´¥ã  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í« > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í« ñáÉBASIC statements are not implemented yetÍ3Éåõ!"}Í« ñáÉunknown@7D179}Ý!‰ÍõÉÝ!…Ãõåõ!~Í« ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2/hardwareconfig.xml0000644000175000017500000000473312262345041024004 0ustar manuelmanuel00000000000000 C-BIOS MSX2 2003 An MSX2 machine using C-BIOS, with an international keyboard layout and 50Hz interrupt frequency. MSX2 largest adb9411124bbb98996c0e494e2ad26840f62548d roms/cbios_main_msx2.rom 2fcb40413e7d373f0f2dbdc815ce18746ddf3684 roms/cbios_sub.rom 512 16000 false int true false false V9938 128 YM2149 21000 cbios-msx2.cmos openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_BR/0000755000175000017500000000000012262345041020652 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_BR/roms/0000755000175000017500000000000012262345041021632 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_BR/roms/cbios_main_msx1_br.rom0000644000175000017500000010000012262345041026076 0ustar manuelmanuel00000000000000óà ¿˜˜Ãíÿ#ÃÿÃ$ÃÃ4$Ã!Ãs$Ã'!Ã9ÃæÃNÃXÃÃ"Ã.ÃEÃKÃSÃ^ÃkÃ~ÑäÃËÃ^ÃÃtôÃ÷Ã5ÃéÃØà ÃaÃtÃ~ÉÃ:ÃRÃ\ÃjÃ|ÃôÃÙÃÃÃ6ÃTÃKÃ%ÃAÃSÃVçÃfÃjÃ{ÃßÃÃÃGÃYòÃÅÃ×ÃéÃüÃÃ!ÃjÃyÃ6ÃHÃYÃhÃxÉÛëÃæÃôà à Ã) Ã9 ÃK Ã] Ão À çÃaÃjÃmÃpÃsÂÆÊóÃÅÃÉÃßÃrÉO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóx wáÉÍSÛ˜ÉõÍ^ñÓ˜Éó}Ó™|æ?Ó™ûÉó}Ó™|æ?ö@Ó™ûÉõÍ^ xAO ñÓ˜ü ùÉÍSåë xA<˜í²= ûáÉëÍ^ë xA<˜í³= ûëÉþÐ!­Ãt´÷5ó!ßó™€í£xíQ· ÷ûÃ":¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍ^ñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍ?:¯üþ8*(ù%%:éóæÍÂ*&ù¯ÍkÉ:¯üþ8ÙÑ*(ùÍ^:éóW ó{Ó˜>Ó˜yÓ˜ Í~0 zÓ˜èûÉÍ>2¯ü2°ü:®ó2°ó>2Üó2Ýó*³ó""ù*·ó"$ù*¹ó"(ù*»ó"&ùÍËÍ͚ͿõÍ>2¯ü2°ü>2Üó2ÝóÍË*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍš:¯ó2°óÍ©ÍJÍÎõÍ>2¯üÍË*Çó""ùÍ^¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ùÍØÍJÍêõÍ>2¯üÍË*Ñó""ùÍ^¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ùÍ ÍJÍõ:ßóæñGÍ.:àóæçöG Í.³ó¯Í< ¯Í<É:ßóæñGÍ.:àóæçG Í.½ó¯Í<¯Í<¯Í<¯Í<¯Í<É:ßóæñöGÍ.:àóæçG Í.Çó¯Í<>Í<>Í<¯Í<¯Í<É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í<¯Í<¯Í<¯Í<¯Í<ÉÕõ!Z Fë~#fo)üGñ°GÍ.Ñ É  &o)))Í~0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍí[¹üíK·üÍ«:éó2òó**ùíKËó @ü:¹üæO Íð :¹ü/æOÍ*·ü "·üñÁÑáÉ:·üæõÅÕåÍ:·üæ(:,ù/2,ùá ÑÁñÍ:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍEõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°ÍKÝñæð°ÍKÚAŽÓ™á}Ó™ÉÍ×Û˜ÉõÍáñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍÎ>!Ík>õ!  ÍkõÍ.!¿Í‘ÉÉ!¿í[$ùÑÀ:¯üþÐå!·ÍáÉ¿Îê:°óþ(À8€*"ù> Ík>!²ûw³ûí°ÃE¯*$ùoÅÍkÁ:êó*ÉóÃk:êóæG°*$ùÃkíK ùo&))) @üÅÕå:ùÍ¿#áÑÁ#ïÉåõ!AÍ‘ ñáÉRIGHTCåõ!SÍ‘ ñáÉLEFTCåõ!dÍ‘ ñáÉUPCåõ!sÍ‘ ñáÉTUPCåõ!ƒÍ‘ ñáÉDOWNCåõ!”Í‘ ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!ÿÍ‘ ñáÉSTORECåõ! Í‘ ñáÉSETATRåõ!# Í‘ ñáÉREADCåõ!4 Í‘ ñáÉSETCåõ!D Í‘ ñáÉNSETCXåõ!V Í‘ ñáÉGTASPCåõ!h Í‘ ñáÉPNTINIåõ!z Í‘ ñáÉSCANRåõ!‹ Í‘ ñáÉSCANL>#Ó.Í >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ¥ ñÍ¥ É|͵ }͵ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Â%ÉxÓ¨y2ÿÿÙ!óùÍÍCÍ?ûÍN!€ÅåÕ:ÁüÍ¿#áÑÁ¾ ë#ìÝ!€ý*ÀüÍ4$>2êó2ëóÍ´!V%ÍŽûxÍŠ>2ëó>2êó>2éó>2¯óÍ´!V%ÍŽÍ"ÍÚþÍ>2™ý¯2)ûÍËþ!ü%ÍŽÃgC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@ÍiÌv!€ÍiÌvË(ÆËg(äá#<æ ÛÉGÍ¿##õxÍ¿##Wñ_xÉåÍY!ABÍ!xáÉåõ!Š%ÍŽñõGæÆ0Í´xËx(>.Í´xæÆ0Í´> Í´> Í´ñá##Íø(OÕÝáõýáÛ™·ú¶õåÍ4$óáñÍø(ËéÍø(ËñÍø(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍYz³xÁÉ!Éü@~Ë(å!%ÍŽá#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!;%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!´"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉvýÉ:¯üþ0 ~·ÈÍ´#÷~·ÈÝ!‰ÍA#ó :¯ü·À(:°óþ)ØPÉÅõÍ«:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!øÍ‘ ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!2Í‘ ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃ4$>ÍRÃ:åõ!cÍ‘ ñáÉINIFNKåõ!uÍ‘ ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõÍñõÍÚÍ:Ýó2aöñÁÑáÉÍÐ(õ:§ü·Â½ñþ 8*þÊöͽÍK*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍlÃ. !ˆÃ> Íò:Ýóæþ òÉ*Üó:±ó,½0 åÍEÍá-"ÜóÉ!"ÜóÉÍ.*ÜóåÍl:Üó!±ó¾0Í.Í.ïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!¬Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍl!±û:Üó_w:°ó ͽÃkÍl*ÜóåE:±ó2ÜóG:Üóͽë=2ÜóͽÍÎðá"Üó&:±óW•=O!±ûëbk+í¸Ã.Íl*ÜóåE:±óG:Üóͽë<2ÜóͽÍÎðÍ.á"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOÍÜÁñÉåÕÅüÍ~ÁÑÕÅ!üÍ‘Áá ëá É:ÝóþÈ> ÍòÃ’:©üþÀ:¯üþÐ:ÌûͽÃK:©üþÀ:¯üþÐͽÍE2Ìû§_ËËËËË˯!¯ü¾ *·ó*ÁóåüÍ~:ªüþ !ü!ü~/w#úáøë!üÍ‘ͽ>ÿÃKV’  . E ¨ lrx’§°j¨E¨K.JLl+LLMY A§B°CD"HExyͶÿõÍ%8Í(öñ > Íö¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§ÊT>2ÝóÃT? Íàý!HÍŽ*Ýó"Êû°û&}wÍþÊäþ 0!åͯ2¨ü2ªüãõ:¨ü§Ä‹ñßÉÍ*Üó"¼ö> 2§öͽÍEþ (*Üó:°ó¼ &±û~·(:§öͽÍK*¼ö"ÜóÉÃ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉÖ×ÚÛÜÝÞáâãÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!LÍ‘ ñáÉISCNTCÃAåõ!aÍ‘ ñáÉBEEP"ÜóÉåõ!uÍ‘ ñáÉFNKSBåõ!†Í‘ ñáÉERAFNKåõ!˜Í‘ ñáÉDSPFNK:¯üþØ:°üͽý·Êtôåõ!¾Í‘ ñá7ÉTAPIONåõ!ÑÍ‘ ñá7ÉTAPINåõ!âÍ‘ ñáÉTAPIOFåõ!õÍ‘ ñá7ÉTAPOONåõ!Í‘ ñá7ÉTAPOUTåõ!Í‘ ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>ÍR<ÍR<ÍR<¸>ÍRÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Ís/æå!ò_~áÁ§ÉåÕ=(Ëó>óÍ\û濳_>ÍR>óÍ\û/æ!âO ~ÑáÁ§É>Á§Éþ0· >Ísöþ<Éó=ÕGæ(L>Í\濳_>ÍRxæ( >óÍ\ûÑ (>ÿɯÉåõ!SÍ‘ ñá¯ÉGTPADåõ!dÍ‘ ñáÉGTPDLÍšF#~##¦o&ÉÍš~2öó¯Í/æ2èóÍc*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍc>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!‹&!»&Í–!«'ݾĶ/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(Ãyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍ"ÑôÑÁá ~§(åÍ"áñì*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍAÝáÉÙõÅÕåíWõÙýå:øúõýáÍ4$ýáÙñâ`ûáÑÁñÙÉþÍÚþï%Éåõ!‚Í‘ ñáÔ%ÉCALBASÛ™!Ó' ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!»%~Ó˜#~§ ùÓ˜§ ùÃgüÍø#_EñæÍø#GÛ¨W£°áÍ€ó{ÑõËzÄ%ñáÁÉÈüÉåWÕ§óüÞ$ÁáÕå|æoG>üÍø#_EzæÍø#GÛ¨W£°áYÍ…óÑåËzÄ%áÉÙóýåñÝåáW§üÞ$ÕzæGüÝåñæ‡(ËË= ù!g$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ%ÙÉóåoæG>«ÆUò{$W|ægG>Àò‹$_/Oz£G}§òÖ$æåÅG>«ÆUò¥$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: BR Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-=\[];'`,./abcdefghijklmnopqrstuvwxyz)!@#$%^&*(_+|{}:"~<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ ¬«ºï½ôûìñ »óòļÇÍÜÆÝÈ ÂÛÌÒÀÏ ýüõð÷®¯öþúÁÎÔÖßÊÞÉ ÓÃ×Ë©ÑÅÕÐùªøëŸÙ¿›˜àáç‡îéíÚ·¹å†¦§„—‹Œ”±¡‘³µæ¤¢£ƒ“‰–‚•ˆŠ …Ø­ž¾œâ€è궸䨎™š°’²´¥ã  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í‘ > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í‘ ñáÉBASIC statements are not implemented yetÍŽÉåõ!"}Í‘ ñáÉunknown@7D179}Ý!‰ÍAÉÝ!…ÃAåõ!~Í‘ ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_BR/roms/cbios_logo_msx1.rom0000644000175000017500000004000012262345041025432 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÍo>2êó2ëóÍb*"ù>ÍVÍGŸÍGÍG:éóæG:êóæ°*ÉóÍV*$ù ë0!¼€Í\*$ù ë* Í\*Éó ë0!ìƒÍ\*Éó>ñÍV*"ù„ ë!‡ ÅåÕÍ\á ëá ÁéÉþøþøþ?øþðþüøð?þüøðøøþ?øüþ?ðþþ?þþü?øøøøðððð????øøøøøøðððøøððððððð?Çßøðøøüþ?þþþþüüü|ððððüø?þðüðø?ðþøð|üø?????????ðøøøøøø|<<<ÇÇÇÇããÇÇÇÇüøðð???üþðøøøøøøøøøøøøþ?????~~~~øøøð?~ÇÇÇÇððððÇÇÇÇxxx|??????ðþüüøðøøøøððøþðøü|||||????~~~~þüþðþüøð<ððð🟟Ÿ|~~ã>??øüþ?ð>?üþ?þøñ<ð?üþ|üøø???ððøü?ðøøüþüþ?ðøþ?øþ?þø?ð?þüøð?üþøþ?þþøð ‘‘‘ ‘‘‘‘ ‘ ‘ ‘‘‘‘‘‘ ‘ ‘‘‘‘ ‘‘‘ññññ   ‘‘‘ñññññññññññññññññññññññññññññññ€€€€   ñññññññññññññññññññññññññññññññññññññññ  ññáññááññááñññáñññáññññáññáñññáññááñ€€€€€ááááááááááááááááááááááááááááááááááááááက€€€€€€€„€€‚ƒ„…†‡€€€€€€€€€€€€€€€€ˆ‰Š‹ŒŽŒŒŒŒŒŒŒŒŒŒŒŒŒã‘’“”Œ•–ŒŒŒŒ—ŒŒŒŒŒŒŒŒŒŒŒä˜™šŒŒ›œžŸ ¡¢£¤¥¦Œä§¨©ŒŒª«ŒŒ¬­®¯ŒŒ°±²³³³´ŒäµµŒŒŒ¶ŒŒŒ·¸¹ºŒŒ»¼½¾¾¿ÀŒäÁÂÃŒŒÄÅÅÆÇÈÉÊËÌÍÎÅÅÅÏÐŒäÑÂÒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒäÓÔÕÖרÙÚÛŒŒŒŒŒŒŒŒŒV0.25ä€ÜÝÞÂßàáâââââââââââââââåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1_BR/hardwareconfig.xml0000644000175000017500000000356712262345041024372 0ustar manuelmanuel00000000000000 C-BIOS MSX1 BR 2010 An MSX1 machine using C-BIOS, with Brazillian settings, like 60Hz interrupt frequency. MSX 7ff9dea68aa3d9ad4e465c7b3f72633fc12f595f roms/cbios_main_msx1_br.rom 16000 false int true false false TMS99X8A 16 YM2149 21000 openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+/0000755000175000017500000000000012262345041020343 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+/roms/0000755000175000017500000000000012262345041021323 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+/roms/cbios_sub.rom0000644000175000017500000004000012262345041024004 0ustar manuelmanuel00000000000000CD~·ÉÉÉÉ|ºÀ}»ÉÉÉÉÉÉÉÉÉÉõÅÕåÙõÅÕåÝåýåÝ!8ý*ÀüÍÅýáÝááÑÁñÙáÑÁñíMÉÉÉÉÉÍÖýíEÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃòûÃûÃü ûÃsûÃyûÃgûÊûÃ…û× û×ûé ûÃê ûÃÙ ûÃÉ ûú ûÃÐûÃáûéûà ûóûÃÅûÃûÃ`ûÃ¥ûÃøûÃPûÃûòûÃfûÃûÃûÃ%ûÃu ûÃÊ ûÃÄ ûÃûÃn ûÃMÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃÿûÃÒ ÉÉÉÉÉÉÉÉûÃaûüûÃØûÃïûÃÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃ|ûÃŒûÞûðÉÉÉÉûÃûÃûÃ’ûÃòûÃûÃûÃ%ûÃ6ÉÉÉÉûÃHÉÉÉÉûÃOÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃaûÃ~ÉÉÉO þ#(##øÉN#fié>#Ó.Í">Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ*ñÍ*É|Í:}Í:ÉÅåõW§óüoñáåÕåõ|æoG>ü͉_Eñæ͉GÛ¨W£°áÍ€ó{ÑõËzÄ«ñáÁÉÈüÉåWÕ§óüoÁáÕå|æoG>ü͉_Ezæ͉GÛ¨W£°áYÍ…óÑåËzÄ«áÉÙóýåñÝåáW§üoÕzæGüÝåñæ‡(ËË= ù!øåÛ¨õ¡°ÙÃŒóÙóÑËzÄ«ÙÉóåoæG>«ÆUò W|ægG>Àò_/Oz£G}§ògæåÅG>«ÆUò6£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝé:àóæ¿GÍÿÉ:àóö@GÍÿÉó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ0Û˜ÉõÍBñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍB͈ xAO ñÓ˜ü ùÉ:¯üþ0Í0Í~ åë xA<˜í²= ûáÉë:¯üþ0ÍB͈ ë xA<˜í³= ûëÉþ Ð!¼ÃÅ`¥s ý [ ·  !èÿ:¯üþ0˾Ëþ¯2ìÿ¯2öÿó!ßó™€í£xíQ· ÷>Ó™>‘Ó™!çÿ›í³###>Ó™>‘Ó™›í³ûÃó:¯üþ(:=õ:éóæðo:ëóµGÍÿñÀ:éóæð!êó¶*¿ó õÍBñÓ˜õ x± ÷ñÉ:ëóGÃÿ:¯ü·ÈÍ:¯üþ8*(ù%%:éóæÍi *&ù¯Íi É:¯üþ8ÙÑ*(ù͈ :éóW ó{Ó˜>Ó˜yÓ˜ Í%0 zÓ˜èûÉÍç>2¯ü2°ü¯2õú2öú:®ó2°ó>2Üó2Ýó*³ó""ù*·ó:°óþ)8!"$ù*¹ó"(ù*»ó"&ùÍÍøÍ\ Í ÃÎÍç>2¯ü2°ü>2Üó2ÝóÍ*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍ\ :¯ó2°ó¯2õú2öúÍP͛͞ ÃÎÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù¯2õú2öúÍÍ›ͽ ÃÎÍç>2¯üÍ*Ñó""ùÍB¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ù¯2õú2öúͲÍ›ÍÓ ÃÎ:°óþ)0(:ßóæñGÍÿ:àóæçöG Íÿ³ó¯Íã ¯ÍãÉ:ßóæñöGÍÿ:àóæçöG Íÿ"ù>Íã ¯ÍãÉ:ßóæñGÍÿ:àóæçG Íÿ½ó¯Íã¯Íã¯Íã¯Íã¯ÍãÉ:ßóæñöGÍÿ:àóæçG ÍÿÇó¯Íã>Íã>Íã¯Íã¯ÍãÉ:ßóæñGÍÿ:àóæçöG ÍÿÑó¯Íã¯Íã¯Íã¯Íã¯ÍãÉÕõ! Fë~#fo)üGñ°GÍÿÑ É  &o)))Í%0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍu í[¹üíK·üÍ :éó2òó**ùíKËó @ü:¹üæO Í«ð :¹ü/æOÍ«*·ü "·üñÁÑáÉ:·üæõÅÕåÍ4 :·üæ(:,ù/2,ùá ÑÁñÍ4 :,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍ"õæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í(Ýñæð°Í(ÚAŽÓ™á}Ó™ÉÍ~ Û˜Éõ͈ ñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™ÍÿàÍÿÍÿ€ÍÿÍÿÍÿ>2Üó2ÝóÍž >!ÍV>õ!  ÍVõÍÿ!¿ý*ÀüÝ!\ÍÅûÉÍÿ;ÍÿÍÿÉ!¿í[$ùý*ÀüÝ!\ÍÅûÉÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù:ßóæñöGÍÿ:àóæçG ÍÿÍÿÇó¯Íã>Íã>Íãû >ÍãÏó¯Íã!éÿ6#6Í›Íÿͽ ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçö G Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Íç ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçG Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Í÷ ÃÎÍç>2¯üÍ:ßóæñö GÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÍç>2¯üÍ:ßóöGÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÀ:¯üþ Ðå!} ÍáÉ ž ½ Ó ½ ç ÷  :°óþ(À8€*"ù> ÍV!"Üó}!²ûw³ûí°É¯*$ùoÅÍVÁ:êó*ÉóÃV:êóæG°*$ùÃV:êóæG°!):êóæG°°!:êóæG°!:êó!õÍl ñ,Íb (Í\ !Ô*Í\ !$Í\ :öúg.&Í\ >-Íb >À.Íb Íl É}Íb |óÓ™yö€Ó™ûÉ>ÍÒ 8øÉíK ùo&))) @üÅÕå:ùÍPáÑÁ#ïÉåõ!¢ ÍñáÉRIGHTCåõ!´ ÍñáÉLEFTCåõ!Å ÍñáÉUPCåõ!Ô ÍñáÉTUPCåõ!ä ÍñáÉDOWNCåõ!õ ÍñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!`ÍñáÉSTORECåõ!rÍñáÉSETATRåõ!„ÍñáÉREADCåõ!•ÍñáÉSETCåõ!¥ÍñáÉNSETCXåõ!·ÍñáÉGTASPCåõ!ÉÍñáÉPNTINIåõ!ÛÍñáÉSCANRåõ!ìÍñáÉSCANLåõ!ýÍñáÉDOGRPHÅÕåÍu :éó2òó*·ü"fõ "·ü:öú*¹üg"hõíCjõíClõ!@üË:òó8:êó2nõÍÂ:ûæö°íy>¬Ó™>‘Ó™û!@üË:òó8:êóÓ› ñ#ìáÑÁÉåõ!~ÍñáÉMAPXYCåõ!ÍñáÉTRIGHTåõ!¢ÍñáÉTRIGHT:öúWÕíCfõíShõ*³ü§íB"jõ!"lõ¯2oõ:òó2nõÍý*µü:öúg"hõÍýÑíShõ>2oõ:µü“2jõÍý*³ü"fõÍýÉÍÂ:ûæöpíyûÉ*µüç0ë§íR#"lõ:öúWíShõÅÑ*³üç0ë§íR#"jõíSfõ¯2oõ:òó2nõÍÂ:ûæö€íyûÉåõ!ZÍñáÃn CLRTXTåÕÅ*"ùËË:õú„öGÍÿ:¯üþ :åóæG:õú°GÍÿ:õúO:¯üþ Ë!¯*(ù)±o>´GÍÿE ÍÿÁÑáÉåÍ'͈ Íÿ !\~Ó˜#ÓšøáÉåÍ'Í~ áÍÿ Û˜ÓšúÉåõÍ'ñ‡O Í~ Û˜GÛ˜OáÉõÅåÍ'z‡O ͈ áBÍÿÁñÓšÓ˜{ÓšÓ˜É:¯ü·Ì>ÍÒ 8øó> Ó™>‘Ó™›!bõí³Éx±7Èz³7Èå!:¯üæþ:¯ü $·íBáØå!Ô·íRáÉíKjõí[lõÍÜØÍÂ~æöíyû·É*bõN#F#íCjõ^#V#íSlõÍÜØþ( þ(N¯CËËø2nõBÅåÍÂ~æö°íy>¬Ó™>‘Ó™ûáÁ>ÍÒ ËGÈË(ôx§(ůCËËøÓ›yÁOî#NBÙ*fõíKjõí[lõq#p#s#r#ÍÜØþ( þ(åÍÂ~æ> íyûáB>ÍÒ Ë ËG óÅ>ÍÒ CËü±ÁOãq#>ÍÒ 8ÖÉåõ!ýÍñáÉBLTVDåõ!ÍñáÉBLTDVåõ!ÍñáÉBLTMDåõ!0ÍñáÉBLTDMåõ!AÍñáÉNEWPADͳͼÉåõ!ZÍñáÉKNJPRTÅ> Ó´yæGÛµ°ÁÓµyæÓ´ÛµæÉÅGÅ> Ó´yæGÛµ°ÁÓµyæÓ´xæÓµÁÉopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+/roms/cbios_music.rom0000644000175000017500000004000012262345041024333 0ustar manuelmanuel00000000000000ABAPRLOPLLÃ%AÃMAÃuAÃAÃÄAÃìAÃBåõ!0AÍ#Ó.ÍHB>Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍPBñÍPBÉ|Í`B}Í`BÉÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+/roms/cbios_main_msx2+.rom0000644000175000017500000010000012262345041025160 0ustar manuelmanuel00000000000000óà ¿˜˜Ã’ÃÒ#äÃ$ÃÀÃG$ÃÆÆ$ÃÌ¡ÃÞÚÃóÃýÃÃ"Ã.ÃZÃ`ÃhÃzÃŽíÃÌÃëÃôÃÃ>ÃæïøÃÁÃÃHÃ{ÃÑÃäÃîÃùÃîÃÃÃÃ!Ã4ÃYÃ÷ÃÅÃêÃÃÿÃÙÃõÃà Ã2ÃÃÃ/ÃAÃSÃCöÃûà ÃfÃyËÃðÃÃÃÕÃÃ-ÃYÃkÃ|ËÛìþÃÎà à Ã) Ã; ÃL Ã\ Ãn À Ã’ ã Ã[ÃÃÃ!Ã$Ã'Ã6Ã:Ã>ÃgÃyÃ}ÓÃ&ÃïÃõÃÞÃ5Ã=Ã;ÃPÃZÖÜÃNÃRO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóyþ8!ßÿþ8 ( þ0!áÿx wáÉÍhÛ˜ÉõÍzñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍzÍZ xAO ñÓ˜ü ùÉ:¯üþ0ÍhÍPåë xA<˜í²= ûáÉë:¯üþ0ÍzÍZë xA<˜í³= ûëÉÝåÝ!ÑÃï:¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍzñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍh:¯üþ8*(ù%%:éóæÍ;*&ù¯Í;É:¯üþ8ÙÑ*(ùÍZ:éóW ó{Ó˜>Ó˜yÓ˜ Íî0 zÓ˜èûÉÝåÝ!ÕÃïÝåÝ!ÙÃïÝåÝ!ÝÃïÝåÝ!áÃï:°óþ)0(:ßóæñGÍ.:àóæçöG Í.³ó¯Í¬ ¯Í¬É:ßóæñöGÍ.:àóæçöG Í."ù>ͬ ¯Í¬É:ßóæñGÍ.:àóæçG Í.½ó¯Í¬¯Í¬¯Í¬¯Í¬¯Í¬É:ßóæñöGÍ.:àóæçG Í.Çó¯Í¬>ͬ>ͬ¯Í¬¯Í¬É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í¬¯Í¬¯Í¬¯Í¬¯Í¬ÉÕõ!Ê Fë~#fo)üGñ°GÍ.Ñ É  &o)))Íî0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ(+þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÝåÝ!‰ÃïÉñåÕÅõÍ7í[¹üíK·üÍÎ:éó2òó**ùíKËó @ü:¹üæO Í}ð :¹ü/æOÍ}*·ü "·üñÁÑáÉ:·üæõÅÕåÍ:·üæ(:,ù/2,ùá ÑÁñÍ:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍZõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í`Ýñæð°Í`ÚAŽÓ™á}Ó™ÉÍPÛ˜ÉõÍZñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍc>!ÍŽ>õ!  ÍŽõÍ.!¿ÍÌÉÍ.;Í.Í.É!¿í[$ùÃÌÀ:¯üþ Ðå!BÍáÉTc•©¹ÌÜ:°óþ(À8€*"ù> ÍŽ>!²ûw³ûí°Ãê¯*$ùoÅÍŽÁ:êó*ÉóÃŽ:êóæG°*$ùÃŽ:êóæG°!):êóæG°°!:êóæG°!:êó!õÍ.ñ,Í$(Í!Ô*Í!$Í:öúg.&Í>-Í$>À.Í$Í.É}Í$ |óÓ™yö€Ó™ûÉ>ͤ8øÉíK ùo&))) @üÅÕå:ùÍÒ#áÑÁ#ïÉåõ!dÍ´ ñáÉRIGHTCåõ!vÍ´ ñáÉLEFTCåõ!‡Í´ ñáÉUPCåõ!–Í´ ñáÉTUPCåõ!¦Í´ ñáÉDOWNCåõ!·Í´ ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!" Í´ ñáÉSTORECåõ!4 Í´ ñáÉSETATRåõ!F Í´ ñáÉREADCåõ!W Í´ ñáÉSETCåõ!g Í´ ñáÉNSETCXåõ!y Í´ ñáÉGTASPCåõ!‹ Í´ ñáÉPNTINIåõ! Í´ ñáÉSCANRåõ!® Í´ ñáÉSCANL>#Ó.ÍÀ >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍÈ ñÍÈ É|ÍØ }ÍØ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Ù%ÃVxÓ¨y2ÿÿÙ!óùÍSÍ—ÍÞ͸ûÍóO!€ÅåÕ:ÁüÍÒ#áÑÁ¾ ë#ìÝ!€ý*ÀüÍG$>2êó2ëóͦ!i%Í3ó>Ó™>ŽÓ™¯Ó™ö@Ó™>vÓ˜¯Ó™!üúÓ™Û˜þv(ËÎËÖ¯Ó™>ŽÓ™ûxÍ/Í¿#>2ëó>2êó>2éó>2¯óͦÝ!AÍõ!i%Í3Í^ÍÚþÍ=>2™ý¯2)ûÍËþ!&Í3ÃC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@Í¥̲!€Í¥̲Ë(ÆËg(äá#<æ ÛÉGÍÒ##õxÍÒ##Wñ_xÉåÍ•!ABÍÆxáÉåõ!¡%Í3ñõGæÆ0ÍYxËx(>.ÍYxæÆ0ÍY> ÍY> ÍYñá##Í4(OÕÝáõýáÛ™·úòõåÍG$óáñÍ4(ËéÍ4(ËñÍ4(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍ•z³xÁÉ!Éü@~Ë(å!´%Í3á#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°>!çÿwèÿí°>2çÿ>2èÿ!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!N%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!Y"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉ!ÁüÅåy¶Ë Í ÍáÁØ# é¯2øúÉÅÍ ÁØÆöÉO2øú!Í&þC #Í&þDy7È·ÉyÅåÍÒ#áÁÉvýÉ:¯üþ0 ~·ÈÍY#÷~·ÈÝ!‰Íõ#ó :¯ü·À(:°óþ)ØPÉÅõÍP:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!Í´ ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!×Í´ ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃG$>ÍÃîåõ!Í´ ñáÉINIFNKåõ!Í´ ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõ͸ñõÍÍÍ:Ýó2aöñÁÑáÉÍÅÐ(õ:§ü·Âbñþ 8*þʪÍbÍ`*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍÃÓ !<Ã> Í—:Ýóæþ òÉ*Üó:±ó,½0 åÍêÍ5á-"ÜóÉ!"ÜóÉÍÓ*ÜóåÍ:Üó!±ó¾0ÍÓÍÓïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!`Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍ!±û:Üó_w:°ó ÍbÃŽÍ*ÜóåE:±ó2ÜóG:ÜóÍbë=2ÜóÍbÍsðá"Üó&:±óW•=O!±ûëbk+í¸ÃÓÍ*ÜóåE:±óG:ÜóÍbë<2ÜóÍbÍsðÍÓá"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOþ)8 (Í:°óÖ(OÍÁñÉåÕÅüÍ­ÁÑÕÅ!üÍÌÁá ëá É:ÝóþÈ> Í—Ã7:©üþÀ:¯üþÐ:ÌûÍbÃ`:©üþÀ:¯üþÐÍbÍZ2Ìû§_ËËËËË˯!¯ü¾ *·ó*ÁóåüÍ­:ªüþ !ü!ü~/w#úáøë!üÍÌÍb>ÿÃ` 7 Ä Ó ê 3 7LUj3E3KÓJñlÐLñM5Y±ALBUCºDÇHêx©y­ͶÿõÍÙ8Í·(öñ > ͪ¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§Ê>2ÝóÃ? Íàý!üÍ3*Ýó"Êû°û&}wÍ4þʘþ 0!™Í¯2¨ü2ªüãõ:¨ü§Ä?ñßÉ͸*Üó"¼ö> 2§öÍbÍZþ (*Üó:°ó¼ &±û~·(:§öÍbÍ`*¼ö"ÜóÉÃÍ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉŠ‹Ž‘’•–—ÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!Í´ ñáÉISCNTCÃõåõ!Í´ ñáÉBEEP"ÜóÉåõ!)Í´ ñáÉFNKSBåõ!:Í´ ñáÉERAFNKåõ!LÍ´ ñáÉDSPFNK:¯üþØ:°üͽý·Êæåõ!rÍ´ ñá7ÉTAPIONåõ!…Í´ ñá7ÉTAPINåõ!–Í´ ñáÉTAPIOFåõ!©Í´ ñá7ÉTAPOONåõ!¼Í´ ñá7ÉTAPOUTåõ!ÎÍ´ ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>Í<Í<Í<¸>ÍÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Í'/æå!¦_~áÁ§ÉåÕ=(Ëó>óÍû濳_>Í>óÍû/æ!–O ~ÑáÁ§É>Á§Éþ0· >Í'öþ<Éó=ÕGæ(L>Í濳_>Íxæ( >óÍûÑ (>ÿɯÉåõ!Í´ ñá¯ÉGTPADåõ!Í´ ñáÉGTPDLÍNF#~##¦o&ÉÍN~2öó¯Í¶/æ2èóÍ*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍ>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!¢&!Ò&ÍJ!Â'ݾÄj/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(ÃÉyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍÖÑôÑÁá ~§(åÍÖáñÃ`*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍõÝáÉÙõÅÕåíWõÙýå:øúõýáÍG$ýáÙñâûáÑÁñÙÉþÍÚþ&ÃVåõ!6Í´ ñáë%ÃVCALBASí[ÜóÕ"ÜóÍÓÑíSÜóÉÛô/É/ÓôÉÛ™!ê' ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!Ò%~Ó˜#~§ ùÓ˜§ ùÃüÍ $_EñæÍ $GÛ¨W£°áÍ€ó{ÑõËzÄ-%ñáÁÉÈüÉåWÕ§óüñ$ÁáÕå|æoG>üÍ $_EzæÍ $GÛ¨W£°áYÍ…óÑåËzÄ-%áÉÙóýåñÝåáW§üñ$ÕzæGüÝåñæ‡(ËË= ù!z$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ-%ÙÉóåoæG>«ÆUòŽ$W|ægG>Àòž$_/Oz£G}§òé$æåÅG>«ÆUò¸$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: EU/INT Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-=\[];'`,./abcdefghijklmnopqrstuvwxyz)!@#$%^&*(_+|{}:"~<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ ¬«ºï½ôûìñ »óòļÇÍÜÆÝÈ ÂÛÌÒÀÏ ýüõð÷®¯öþúÁÎÔÖßÊÞÉ ÓÃ×Ë©ÑÅÕÐùªøëŸÙ¿›˜àáç‡îéíÚ·¹å†¦§„—‹Œ”±¡‘³µæ¤¢£ƒ“‰–‚•ˆŠ …Ø­ž¾œâ€è궸䨎™š°’²´¥ã  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í´ > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í´ ñáÉBASIC statements are not implemented yetÍ3Éåõ!"}Í´ ñáÉunknown@7D179}Ý!‰ÍõÉÝ!…Ãõåõ!~Í´ ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+/roms/cbios_logo_msx2+.rom0000644000175000017500000004000012262345041025177 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÍzØ>€Í}À!RƒÍØ‚!"êó>Í_!€ÍEÍA!"êóÍb:èÿæG ÍG>Ý!1Í_ËG óÕ!qí°á Àwëó> Ó™>‘Ó™å›!qí³á>¬Ó™>‘Ó™ûÿí³>Ý!1Í_ËG(í³ï À!€í°!°"·ü!a"¹ü>2éó>2û!Ò‚ÍT2ûÍD !ÀæðO~æð¹(0ÆÖO~æ±wæO~æ¹(0<=O~æð±w#O~¹(0<=w#¿!ÀÍEvý  !À¾  #ø !À‚^#V#ÅåëÍEáÁvvðÉó¯Ó™>Ó™š í³ûÉ:¯üþ0 ~·ÈÍ¢#÷~·ÈÝ!‰Í_#óUð''''''''''''''''3Uwtcsrrrpp'3Uwtcsrrrpw'3Uwtcsrrrwp'3Uwtcsrrwpp'3Uwtcsrwrpp'3Uwtcswrrpp'3Uwtcwrrrpp'3Uwwcsrrrpp'3UwtwsrrrppÀà‚ ‚@‚`‚€‚ ‚ V0.25Ù€ÙÙÍ.ƒÙ8í õ~#·(0Ù_P0Í.ƒËÍ.ƒËÍ.ƒËÍ.ƒ8Ë»Í;ƒåÙåÕÙbkÁíBÁí°áÀÍ:ƒØåÙåÙÁbk+í°á¯Ë!ÀÙ~#ÙOË! ÉÙ!Í.ƒ0øÍ.ƒíjù#ÙÉ~3Á@ý¢Cy6©™4}cû:™šj»º…$“ƒ¿a~ªª»ª©€?®Ç~«~Œƒ¬€Y~ï€×g~}¬€[~âªÙ‰j€+…~˜CèrºªµýW‰ˆcº“PzùüïföÙ¶÷a 8˜š€Mû–9¿ö<‘ˆ€Ò©Ž7õ_ ”™ö~€ž&ŽþŒ9›á¹l戗€ßü¤™ìóë‚8Š»€_wòcþ¸êõ–€f““Aþ€®Š1ïüŒþ|³‰‰øŸ“Ï™ÌWéìþé9œ~ÁÉ“ý´þ|+fNá|6Á¹œþ!WwvNð¾5?à¥þâ|Vgå<Ì(3ÿó|fGUVòdCÌþüéwcÿý93þ?“µ:î3kþ/õ”ýÕý¹õïyÄŠw çΰ:z?c!8óÎDw{gKW”Q—\#vǯÈ'C>þ§ã·R•gç~xfÇ€Y+#yþSÝ–ÙézÈYgeUÜn8vcç9FV¡#f‘Œ+zUþ8Cø’VÜçgsfþE˜ÞHg™åf‡-ÓÿèÝýÿ=ÉÒfã_of1ë¾'< Û­Ïøi;þø¶ëYðfðŽ‹c™ËØ#C=þ—ÜûúKñ^fNf£þÒ™7z;—èS þä½Ü8;þcÝúߨ¸®×ý†ÿ8÷ù~f¥‰)Gfí×þËð_úf6§ê{©¯Üÿ‘WîVØêÉþvž”iDòNÕ>seþ×ø þù>·sÄVÞ ¿JþþOÎÓ¡ý}¿):ü€ûé.Óò¢Gý—”5ø©8ÿé“ûŽ3ûôÒ”€Züÿ|¨4ΞSf£EfC¯i*döODFd~Cäÿ| 6ÿÒWvÿ>-w—­ÛÏþfåûëÁùóÿòHåe—þ|¨%éEVSþ.òêÌ­üÿû~æ4FPpD'VUêÕfþ?ð2ÿïþ)þÝS™´þ¦ù|rÿùh®ÿüþ’eþGÿ)4Uþ„TCÿ~é3ŽÿÏ·e6éÍ€uS˜{|¤[ÿä÷ÿÉëe7UeDUgU˜eþ|˜“áøoŠ¿Ï*ü¶eUŽ…$€eUS¥ÊUgþ’¯GDä«8€âÍÍr•ý§S…(ˆÉ€6áê%ÇgDÔNÿøV€ògbŒãŒD~RdÔ͉ ¼ìàUŽ»šàÏ0Þó§"õqUbíÄ€EæfÎ ¼ü~?8€_ͺ9CËÇgê~73ä߀Íü7áÛ€ƒãûk€mîý­€8?ï³R­:Šók€>ÿìç€é:?ÿþ~€~dÿ–ÿøS€¤9Ÿé|QþüâJ€º™jê>~ë•öÿ€^(þþñ/e€ÁÿþÆ~/‚$€¸½ÿ~–ÿÿüü€®)šÿðæ?ô þ})ƒ8¯ÿãcôÿô΃$€|‡4"î~3ƒ€>A3@ý"D€>_~éå€c€â?30~¦–€#€^ª}C¯34~úˆ€ì8ªKªƒ#úªó~úȀ爃Lc¯D~úÈ€êƒSƒ4D~úø€ï¿~ì-…ž@ícûú/ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2+/hardwareconfig.xml0000644000175000017500000000604512262345041024055 0ustar manuelmanuel00000000000000 C-BIOS MSX2+ 2005 An MSX2+ machine using C-BIOS, with MSX-MUSIC, an international keyboard layout and 50Hz interrupt frequency. MSX2+ largest e73ceb475e3f91492e700f45809c9b328dbee3fd roms/cbios_main_msx2+.rom 2fcb40413e7d373f0f2dbdc815ce18746ddf3684 roms/cbios_sub.rom 5c5eb001e6a1fe29edb7abd428a3967bb388e5db roms/cbios_music.rom 9000 512 16000 false int true false false V9958 128 YM2149 21000 cbios-msx2+.cmos true openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1/0000755000175000017500000000000012262345041020267 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1/roms/0000755000175000017500000000000012262345041021247 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1/roms/cbios_logo_msx1.rom0000644000175000017500000004000012262345041025047 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÍo>2êó2ëóÍb*"ù>ÍVÍGŸÍGÍG:éóæG:êóæ°*ÉóÍV*$ù ë0!¼€Í\*$ù ë* Í\*Éó ë0!ìƒÍ\*Éó>ñÍV*"ù„ ë!‡ ÅåÕÍ\á ëá ÁéÉþøþøþ?øþðþüøð?þüøðøøþ?øüþ?ðþþ?þþü?øøøøðððð????øøøøøøðððøøððððððð?Çßøðøøüþ?þþþþüüü|ððððüø?þðüðø?ðþøð|üø?????????ðøøøøøø|<<<ÇÇÇÇããÇÇÇÇüøðð???üþðøøøøøøøøøøøøþ?????~~~~øøøð?~ÇÇÇÇððððÇÇÇÇxxx|??????ðþüüøðøøøøððøþðøü|||||????~~~~þüþðþüøð<ððð🟟Ÿ|~~ã>??øüþ?ð>?üþ?þøñ<ð?üþ|üøø???ððøü?ðøøüþüþ?ðøþ?øþ?þø?ð?þüøð?üþøþ?þþøð ‘‘‘ ‘‘‘‘ ‘ ‘ ‘‘‘‘‘‘ ‘ ‘‘‘‘ ‘‘‘ññññ   ‘‘‘ñññññññññññññññññññññññññññññññ€€€€   ñññññññññññññññññññññññññññññññññññññññ  ññáññááññááñññáñññáññññáññáñññáññááñ€€€€€ááááááááááááááááááááááááááááááááááááááက€€€€€€€„€€‚ƒ„…†‡€€€€€€€€€€€€€€€€ˆ‰Š‹ŒŽŒŒŒŒŒŒŒŒŒŒŒŒŒã‘’“”Œ•–ŒŒŒŒ—ŒŒŒŒŒŒŒŒŒŒŒä˜™šŒŒ›œžŸ ¡¢£¤¥¦Œä§¨©ŒŒª«ŒŒ¬­®¯ŒŒ°±²³³³´ŒäµµŒŒŒ¶ŒŒŒ·¸¹ºŒŒ»¼½¾¾¿ÀŒäÁÂÃŒŒÄÅÅÆÇÈÉÊËÌÍÎÅÅÅÏÐŒäÑÂÒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒŒäÓÔÕÖרÙÚÛŒŒŒŒŒŒŒŒŒV0.25ä€ÜÝÞÂßàáâââââââââââââââåÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1/roms/cbios_main_msx1.rom0000644000175000017500000010000012262345041025030 0ustar manuelmanuel00000000000000óà ¿˜˜Ãíÿ#ÃÿÃ$ÃÃ4$Ã!Ãs$Ã'¡Ã9ÃæÃNÃXÃÃ"Ã.ÃEÃKÃSÃ^ÃkÃ~ÑäÃËÃ^ÃÃtôÃ÷Ã5ÃéÃØà ÃaÃtÃ~ÉÃ:ÃRÃ\ÃjÃ|ÃôÃÙÃÃÃ6ÃTÃKÃ%ÃAÃSÃVçÃfÃjÃ{ÃßÃÃÃGÃYòÃÅÃ×ÃéÃüÃÃ!ÃjÃyÃ6ÃHÃYÃhÃxÉÛëÃæÃôà à Ã) Ã9 ÃK Ã] Ão À çÃaÃjÃmÃpÃsÂÆÊóÃÅÃÉÃßÃrÉO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóx wáÉÍSÛ˜ÉõÍ^ñÓ˜Éó}Ó™|æ?Ó™ûÉó}Ó™|æ?ö@Ó™ûÉõÍ^ xAO ñÓ˜ü ùÉÍSåë xA<˜í²= ûáÉëÍ^ë xA<˜í³= ûëÉþÐ!­Ãt´÷5ó!ßó™€í£xíQ· ÷ûÃ":¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍ^ñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍ?:¯üþ8*(ù%%:éóæÍÂ*&ù¯ÍkÉ:¯üþ8ÙÑ*(ùÍ^:éóW ó{Ó˜>Ó˜yÓ˜ Í~0 zÓ˜èûÉÍ>2¯ü2°ü:®ó2°ó>2Üó2Ýó*³ó""ù*·ó"$ù*¹ó"(ù*»ó"&ùÍËÍ͚ͿõÍ>2¯ü2°ü>2Üó2ÝóÍË*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍš:¯ó2°óÍ©ÍJÍÎõÍ>2¯üÍË*Çó""ùÍ^¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ùÍØÍJÍêõÍ>2¯üÍË*Ñó""ùÍ^¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ùÍ ÍJÍõ:ßóæñGÍ.:àóæçöG Í.³ó¯Í< ¯Í<É:ßóæñGÍ.:àóæçG Í.½ó¯Í<¯Í<¯Í<¯Í<¯Í<É:ßóæñöGÍ.:àóæçG Í.Çó¯Í<>Í<>Í<¯Í<¯Í<É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í<¯Í<¯Í<¯Í<¯Í<ÉÕõ!Z Fë~#fo)üGñ°GÍ.Ñ É  &o)))Í~0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍí[¹üíK·üÍ«:éó2òó**ùíKËó @ü:¹üæO Íð :¹ü/æOÍ*·ü "·üñÁÑáÉ:·üæõÅÕåÍ:·üæ(:,ù/2,ùá ÑÁñÍ:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍEõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°ÍKÝñæð°ÍKÚAŽÓ™á}Ó™ÉÍ×Û˜ÉõÍáñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍÎ>!Ík>õ!  ÍkõÍ.!¿Í‘ÉÉ!¿í[$ùÑÀ:¯üþÐå!·ÍáÉ¿Îê:°óþ(À8€*"ù> Ík>!²ûw³ûí°ÃE¯*$ùoÅÍkÁ:êó*ÉóÃk:êóæG°*$ùÃkíK ùo&))) @üÅÕå:ùÍ¿#áÑÁ#ïÉåõ!AÍ‘ ñáÉRIGHTCåõ!SÍ‘ ñáÉLEFTCåõ!dÍ‘ ñáÉUPCåõ!sÍ‘ ñáÉTUPCåõ!ƒÍ‘ ñáÉDOWNCåõ!”Í‘ ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!ÿÍ‘ ñáÉSTORECåõ! Í‘ ñáÉSETATRåõ!# Í‘ ñáÉREADCåõ!4 Í‘ ñáÉSETCåõ!D Í‘ ñáÉNSETCXåõ!V Í‘ ñáÉGTASPCåõ!h Í‘ ñáÉPNTINIåõ!z Í‘ ñáÉSCANRåõ!‹ Í‘ ñáÉSCANL>#Ó.Í >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ¥ ñÍ¥ É|͵ }͵ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Æ%ÉxÓ¨y2ÿÿÙ!óùÍÍCÍ?ûÍN!€ÅåÕ:ÁüÍ¿#áÑÁ¾ ë#ìÝ!€ý*ÀüÍ4$>2êó2ëóÍ´!V%ÍŽûxÍŠ>2ëó>2êó>2éó>2¯óÍ´!V%ÍŽÍ"ÍÚþÍ>2™ý¯2)ûÍËþ!&ÍŽÃgC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@ÍiÌv!€ÍiÌvË(ÆËg(äá#<æ ÛÉGÍ¿##õxÍ¿##Wñ_xÉåÍY!ABÍ!xáÉåõ!Ž%ÍŽñõGæÆ0Í´xËx(>.Í´xæÆ0Í´> Í´> Í´ñá##Íø(OÕÝáõýáÛ™·ú¶õåÍ4$óáñÍø(ËéÍø(ËñÍø(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍYz³xÁÉ!Éü@~Ë(å!¡%ÍŽá#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!;%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!´"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉvýÉ:¯üþ0 ~·ÈÍ´#÷~·ÈÝ!‰ÍA#ó :¯ü·À(:°óþ)ØPÉÅõÍ«:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!øÍ‘ ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!2Í‘ ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃ4$>ÍRÃ:åõ!cÍ‘ ñáÉINIFNKåõ!uÍ‘ ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõÍñõÍÚÍ:Ýó2aöñÁÑáÉÍÐ(õ:§ü·Â½ñþ 8*þÊöͽÍK*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍlÃ. !ˆÃ> Íò:Ýóæþ òÉ*Üó:±ó,½0 åÍEÍá-"ÜóÉ!"ÜóÉÍ.*ÜóåÍl:Üó!±ó¾0Í.Í.ïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!¬Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍl!±û:Üó_w:°ó ͽÃkÍl*ÜóåE:±ó2ÜóG:Üóͽë=2ÜóͽÍÎðá"Üó&:±óW•=O!±ûëbk+í¸Ã.Íl*ÜóåE:±óG:Üóͽë<2ÜóͽÍÎðÍ.á"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOÍÜÁñÉåÕÅüÍ~ÁÑÕÅ!üÍ‘Áá ëá É:ÝóþÈ> ÍòÃ’:©üþÀ:¯üþÐ:ÌûͽÃK:©üþÀ:¯üþÐͽÍE2Ìû§_ËËËËË˯!¯ü¾ *·ó*ÁóåüÍ~:ªüþ !ü!ü~/w#úáøë!üÍ‘ͽ>ÿÃKV’  . E ¨ lrx’§°j¨E¨K.JLl+LLMY A§B°CD"HExyͶÿõÍ%8Í(öñ > Íö¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§ÊT>2ÝóÃT? Íàý!HÍŽ*Ýó"Êû°û&}wÍþÊäþ 0!åͯ2¨ü2ªüãõ:¨ü§Ä‹ñßÉÍ*Üó"¼ö> 2§öͽÍEþ (*Üó:°ó¼ &±û~·(:§öͽÍK*¼ö"ÜóÉÃ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉÖ×ÚÛÜÝÞáâãÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!LÍ‘ ñáÉISCNTCÃAåõ!aÍ‘ ñáÉBEEP"ÜóÉåõ!uÍ‘ ñáÉFNKSBåõ!†Í‘ ñáÉERAFNKåõ!˜Í‘ ñáÉDSPFNK:¯üþØ:°üͽý·Êtôåõ!¾Í‘ ñá7ÉTAPIONåõ!ÑÍ‘ ñá7ÉTAPINåõ!âÍ‘ ñáÉTAPIOFåõ!õÍ‘ ñá7ÉTAPOONåõ!Í‘ ñá7ÉTAPOUTåõ!Í‘ ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>ÍR<ÍR<ÍR<¸>ÍRÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Ís/æå!ò_~áÁ§ÉåÕ=(Ëó>óÍ\û濳_>ÍR>óÍ\û/æ!âO ~ÑáÁ§É>Á§Éþ0· >Ísöþ<Éó=ÕGæ(L>Í\濳_>ÍRxæ( >óÍ\ûÑ (>ÿɯÉåõ!SÍ‘ ñá¯ÉGTPADåõ!dÍ‘ ñáÉGTPDLÍšF#~##¦o&ÉÍš~2öó¯Í/æ2èóÍc*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍc>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!&!¿&Í–!¯'ݾĶ/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(Ãyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍ"ÑôÑÁá ~§(åÍ"áñì*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍAÝáÉÙõÅÕåíWõÙýå:øúõýáÍ4$ýáÙñâ`ûáÑÁñÙÉþÍÚþó%Éåõ!‚Í‘ ñáØ%ÉCALBASÛ™!×' ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!¿%~Ó˜#~§ ùÓ˜§ ùÃgüÍø#_EñæÍø#GÛ¨W£°áÍ€ó{ÑõËzÄ%ñáÁÉÈüÉåWÕ§óüÞ$ÁáÕå|æoG>üÍø#_EzæÍø#GÛ¨W£°áYÍ…óÑåËzÄ%áÉÙóýåñÝåáW§üÞ$ÕzæGüÝåñæ‡(ËË= ù!g$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ%ÙÉóåoæG>«ÆUò{$W|ægG>Àò‹$_/Oz£G}§òÖ$æåÅG>«ÆUò¥$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: EU/INT Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-=\[];'`,./abcdefghijklmnopqrstuvwxyz)!@#$%^&*(_+|{}:"~<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ ¬«ºï½ôûìñ »óòļÇÍÜÆÝÈ ÂÛÌÒÀÏ ýüõð÷®¯öþúÁÎÔÖßÊÞÉ ÓÃ×Ë©ÑÅÕÐùªøëŸÙ¿›˜àáç‡îéíÚ·¹å†¦§„—‹Œ”±¡‘³µæ¤¢£ƒ“‰–‚•ˆŠ …Ø­ž¾œâ€è궸䨎™š°’²´¥ã  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í‘ > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í‘ ñáÉBASIC statements are not implemented yetÍŽÉåõ!"}Í‘ ñáÉunknown@7D179}Ý!‰ÍAÉÝ!…ÃAåõ!~Í‘ ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX1/hardwareconfig.xml0000644000175000017500000000357212262345041024003 0ustar manuelmanuel00000000000000 C-BIOS MSX1 2003 An MSX1 machine using C-BIOS, with an international keyboard layout and 50Hz interrupt frequency. MSX d96e4ba798a866ba97eb75c29c9581a13ef3917b roms/cbios_main_msx1.rom 16000 false int false false false TMS9929A 16 YM2149 21000 openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_BR/0000755000175000017500000000000012262345041020653 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_BR/roms/0000755000175000017500000000000012262345041021633 5ustar manuelmanuel00000000000000openmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_BR/roms/cbios_sub.rom0000644000175000017500000004000012262345041024314 0ustar manuelmanuel00000000000000CD~·ÉÉÉÉ|ºÀ}»ÉÉÉÉÉÉÉÉÉÉõÅÕåÙõÅÕåÝåýåÝ!8ý*ÀüÍÅýáÝááÑÁñÙáÑÁñíMÉÉÉÉÉÍÖýíEÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃòûÃûÃü ûÃsûÃyûÃgûÊûÃ…û× û×ûé ûÃê ûÃÙ ûÃÉ ûú ûÃÐûÃáûéûà ûóûÃÅûÃûÃ`ûÃ¥ûÃøûÃPûÃûòûÃfûÃûÃûÃ%ûÃu ûÃÊ ûÃÄ ûÃûÃn ûÃMÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃÿûÃÒ ÉÉÉÉÉÉÉÉûÃaûüûÃØûÃïûÃÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃ|ûÃŒûÞûðÉÉÉÉûÃûÃûÃ’ûÃòûÃûÃûÃ%ûÃ6ÉÉÉÉûÃHÉÉÉÉûÃOÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉÉûÃaûÃ~ÉÉÉO þ#(##øÉN#fié>#Ó.Í">Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ*ñÍ*É|Í:}Í:ÉÅåõW§óüoñáåÕåõ|æoG>ü͉_Eñæ͉GÛ¨W£°áÍ€ó{ÑõËzÄ«ñáÁÉÈüÉåWÕ§óüoÁáÕå|æoG>ü͉_Ezæ͉GÛ¨W£°áYÍ…óÑåËzÄ«áÉÙóýåñÝåáW§üoÕzæGüÝåñæ‡(ËË= ù!øåÛ¨õ¡°ÙÃŒóÙóÑËzÄ«ÙÉóåoæG>«ÆUò W|ægG>Àò_/Oz£G}§ògæåÅG>«ÆUò6£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝé:àóæ¿GÍÿÉ:àóö@GÍÿÉó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ0Û˜ÉõÍBñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍB͈ xAO ñÓ˜ü ùÉ:¯üþ0Í0Í~ åë xA<˜í²= ûáÉë:¯üþ0ÍB͈ ë xA<˜í³= ûëÉþ Ð!¼ÃÅ`¥s ý [ ·  !èÿ:¯üþ0˾Ëþ¯2ìÿ¯2öÿó!ßó™€í£xíQ· ÷>Ó™>‘Ó™!çÿ›í³###>Ó™>‘Ó™›í³ûÃó:¯üþ(:=õ:éóæðo:ëóµGÍÿñÀ:éóæð!êó¶*¿ó õÍBñÓ˜õ x± ÷ñÉ:ëóGÃÿ:¯ü·ÈÍ:¯üþ8*(ù%%:éóæÍi *&ù¯Íi É:¯üþ8ÙÑ*(ù͈ :éóW ó{Ó˜>Ó˜yÓ˜ Í%0 zÓ˜èûÉÍç>2¯ü2°ü¯2õú2öú:®ó2°ó>2Üó2Ýó*³ó""ù*·ó:°óþ)8!"$ù*¹ó"(ù*»ó"&ùÍÍøÍ\ Í ÃÎÍç>2¯ü2°ü>2Üó2ÝóÍ*½ó""ù*Áó"$ù*Åó"&ù*Ãó"(ùÍ\ :¯ó2°ó¯2õú2öúÍP͛͞ ÃÎÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù¯2õú2öúÍÍ›ͽ ÃÎÍç>2¯üÍ*Ñó""ùÍB¯óõõ Ó˜<ûñ ôñÆ ëû*Õó"$ù*×ó"(ù*Ùó"&ù¯2õú2öúͲÍ›ÍÓ ÃÎ:°óþ)0(:ßóæñGÍÿ:àóæçöG Íÿ³ó¯Íã ¯ÍãÉ:ßóæñöGÍÿ:àóæçöG Íÿ"ù>Íã ¯ÍãÉ:ßóæñGÍÿ:àóæçG Íÿ½ó¯Íã¯Íã¯Íã¯Íã¯ÍãÉ:ßóæñöGÍÿ:àóæçG ÍÿÇó¯Íã>Íã>Íã¯Íã¯ÍãÉ:ßóæñGÍÿ:àóæçöG ÍÿÑó¯Íã¯Íã¯Íã¯Íã¯ÍãÉÕõ! Fë~#fo)üGñ°GÍÿÑ É  &o)))Í%0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ("þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÉñåÕÅõÍu í[¹üíK·üÍ :éó2òó**ùíKËó @ü:¹üæO Í«ð :¹ü/æOÍ«*·ü "·üñÁÑáÉ:·üæõÅÕåÍ4 :·üæ(:,ù/2,ùá ÑÁñÍ4 :,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍ"õæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°Í(Ýñæð°Í(ÚAŽÓ™á}Ó™ÉÍ~ Û˜Éõ͈ ñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™ÍÿàÍÿÍÿ€ÍÿÍÿÍÿ>2Üó2ÝóÍž >!ÍV>õ!  ÍVõÍÿ!¿ý*ÀüÝ!\ÍÅûÉÍÿ;ÍÿÍÿÉ!¿í[$ùý*ÀüÝ!\ÍÅûÉÍç>2¯üÍ*Çó""ùÍB¯óÓ˜< ûùû*Ëó"$ù*Íó"(ù*Ïó"&ù:ßóæñöGÍÿ:àóæçG ÍÿÍÿÇó¯Íã>Íã>Íãû >ÍãÏó¯Íã!éÿ6#6Í›Íÿͽ ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçö G Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Íç ÃÎÍç>2¯üÍ:ßóæñöGÍÿ:àóæçG Íÿ!""ù!x"&ù!v"(ù¯2õú2öúÍÿïÍÿ ÍÿÍÿÍ—Í÷ ÃÎÍç>2¯üÍ:ßóæñö GÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÍç>2¯üÍ:ßóöGÍÿ:àóæçG Íÿ!""ù!ð"&ù!ú"(ù¯2õú2öúÍÿ÷Íÿ ÍÿÍÿÍ—Í ÃÎÀ:¯üþ Ðå!} ÍáÉ ž ½ Ó ½ ç ÷  :°óþ(À8€*"ù> ÍV!"Üó}!²ûw³ûí°É¯*$ùoÅÍVÁ:êó*ÉóÃV:êóæG°*$ùÃV:êóæG°!):êóæG°°!:êóæG°!:êó!õÍl ñ,Íb (Í\ !Ô*Í\ !$Í\ :öúg.&Í\ >-Íb >À.Íb Íl É}Íb |óÓ™yö€Ó™ûÉ>ÍÒ 8øÉíK ùo&))) @üÅÕå:ùÍPáÑÁ#ïÉåõ!¢ ÍñáÉRIGHTCåõ!´ ÍñáÉLEFTCåõ!Å ÍñáÉUPCåõ!Ô ÍñáÉTUPCåõ!ä ÍñáÉDOWNCåõ!õ ÍñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ!`ÍñáÉSTORECåõ!rÍñáÉSETATRåõ!„ÍñáÉREADCåõ!•ÍñáÉSETCåõ!¥ÍñáÉNSETCXåõ!·ÍñáÉGTASPCåõ!ÉÍñáÉPNTINIåõ!ÛÍñáÉSCANRåõ!ìÍñáÉSCANLåõ!ýÍñáÉDOGRPHÅÕåÍu :éó2òó*·ü"fõ "·ü:öú*¹üg"hõíCjõíClõ!@üË:òó8:êó2nõÍÂ:ûæö°íy>¬Ó™>‘Ó™û!@üË:òó8:êóÓ› ñ#ìáÑÁÉåõ!~ÍñáÉMAPXYCåõ!ÍñáÉTRIGHTåõ!¢ÍñáÉTRIGHT:öúWÕíCfõíShõ*³ü§íB"jõ!"lõ¯2oõ:òó2nõÍý*µü:öúg"hõÍýÑíShõ>2oõ:µü“2jõÍý*³ü"fõÍýÉÍÂ:ûæöpíyûÉ*µüç0ë§íR#"lõ:öúWíShõÅÑ*³üç0ë§íR#"jõíSfõ¯2oõ:òó2nõÍÂ:ûæö€íyûÉåõ!ZÍñáÃn CLRTXTåÕÅ*"ùËË:õú„öGÍÿ:¯üþ :åóæG:õú°GÍÿ:õúO:¯üþ Ë!¯*(ù)±o>´GÍÿE ÍÿÁÑáÉåÍ'͈ Íÿ !\~Ó˜#ÓšøáÉåÍ'Í~ áÍÿ Û˜ÓšúÉåõÍ'ñ‡O Í~ Û˜GÛ˜OáÉõÅåÍ'z‡O ͈ áBÍÿÁñÓšÓ˜{ÓšÓ˜É:¯ü·Ì>ÍÒ 8øó> Ó™>‘Ó™›!bõí³Éx±7Èz³7Èå!:¯üæþ:¯ü $·íBáØå!Ô·íRáÉíKjõí[lõÍÜØÍÂ~æöíyû·É*bõN#F#íCjõ^#V#íSlõÍÜØþ( þ(N¯CËËø2nõBÅåÍÂ~æö°íy>¬Ó™>‘Ó™ûáÁ>ÍÒ ËGÈË(ôx§(ůCËËøÓ›yÁOî#NBÙ*fõíKjõí[lõq#p#s#r#ÍÜØþ( þ(åÍÂ~æ> íyûáB>ÍÒ Ë ËG óÅ>ÍÒ CËü±ÁOãq#>ÍÒ 8ÖÉåõ!ýÍñáÉBLTVDåõ!ÍñáÉBLTDVåõ!ÍñáÉBLTMDåõ!0ÍñáÉBLTDMåõ!AÍñáÉNEWPADͳͼÉåõ!ZÍñáÉKNJPRTÅ> Ó´yæGÛµ°ÁÓµyæÓ´ÛµæÉÅGÅ> Ó´yæGÛµ°ÁÓµyæÓ´xæÓµÁÉopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_BR/roms/cbios_logo_msx2.rom0000644000175000017500000004000012262345041025434 0ustar manuelmanuel00000000000000C-BIOS Logo ROMÿÀ!HƒÍ΂!"êó>Í_!vÍ;ÍA!"êóÍb:èÿæG ÍG>Ý!1Í_ËG óÕ!gí°á Àwëó> Ó™>‘Ó™å›!gí³á>¬Ó™>‘Ó™ûÿí³>Ý!1Í_ËG(í³ï À!ví°!°"·ü!a"¹ü>2éó>2û!È‚ÍJ2ûÍD–!ÀæðO~æð¹(0ÆÖO~æ±wæO~æ¹(0<=O~æð±w#O~¹(0<=w#¿!ÀÍ;vý –!À¾  #ø !¶‚^#V#ÅåëÍ;áÁvvðÉó¯Ó™>Ó™š í³ûÉ:¯üþ0 ~·ÈÍ¢#÷~·ÈÝ!‰Í_#óUð''''''''''''''''3Uwtcsrrrpp'3Uwtcsrrrpw'3Uwtcsrrrwp'3Uwtcsrrwpp'3Uwtcsrwrpp'3Uwtcswrrpp'3Uwtcwrrrpp'3Uwwcsrrrpp'3Uwtwsrrrpp¶Öö‚6‚V‚v‚–‚–V0.25Ù€ÙÙÍ$ƒÙ8í õ~#·(0Ù_P0Í$ƒËÍ$ƒËÍ$ƒËÍ$ƒ8Ë»Í1ƒåÙåÕÙbkÁíBÁí°áÀÍ0ƒØåÙåÙÁbk+í°á¯Ë!ÀÙ~#ÙOË! ÉÙ!Í$ƒ0øÍ$ƒíjù#ÙÉ~3Á@ý¢Cy6©™4}cû:™šj»º…$“ƒ¿a~ªª»ª©€?®Ç~«~Œƒ¬€Y~ï€×g~}¬€[~âªÙ‰j€+…~˜CèrºªµýW‰ˆcº“PzùüïföÙ¶÷a 8˜š€Mû–9¿ö<‘ˆ€Ò©Ž7õ_ ”™ö~€ž&ŽþŒ9›á¹l戗€ßü¤™ìóë‚8Š»€_wòcþ¸êõ–€f““Aþ€®Š1ïüŒþ|³‰‰øŸ“Ï™ÌWéìþé9œ~ÁÉ“ý´þ|+fNá|6Á¹œþ!WwvNð¾5?à¥þâ|Vgå<Ì(3ÿó|fGUVòdCÌþüéwcÿý93þ?“µ:î3kþ/õ”ýÕý¹õïyÄŠw çΰ:z?c!8óÎDw{gKW”Q—\#vǯÈ'C>þ§ã·R•gç~xfÇ€Y+#yþSÝ–ÙézÈYgeUÜn8vcç9FV¡#f‘Œ+zUþ8Cø’VÜçgsfþE˜ÞHg™åf‡-ÓÿèÝýÿ=ÉÒfã_of1ë¾'< Û­Ïøi;þø¶ëYðfðŽ‹c™ËØ#C=þ—ÜûúKñ^fNf£þÒ™7z;—èS þä½Ü8;þcÝúߨ¸®×ý†ÿ8÷ù~f¥‰)Gfí×þËð_úf6§ê{©¯Üÿ‘WîVØêÉþvž”iDòNÕ>seþ×ø þù>·sÄVÞ ¿JþþOÎÓ¡ý}¿):ü€ûé.Óò¢Gý—”5ø©8ÿé“ûŽ3ûôÒ”€Züÿ|¨4ΞSf£EfC¯i*döODFd~Cäÿ| 6ÿÒWvÿ>-w—­ÛÏþfåûëÁùóÿòHåe—þ|¨%éEVSþ.òêÌ­üÿû~æ4FPpD'VUêÕfþ?ð2ÿïþ)þÝS™´þ¦ù|rÿùh®ÿüþ’eþGÿ)4Uþ„TCÿ~é3ŽÿÏ·e6éÍ€uS˜{|¤[ÿä÷ÿÉëe7UeDUgU˜eþ|˜“áøoŠ¿Ï*ü¶eUŽ…$€eUS¥ÊUgþ’¯GDä«8€âÍÍr•ý§S…(ˆÉ€6áê%ÇgDÔNÿøV€ògbŒãŒD~RdÔ͉ ¼ìàUŽ»šàÏ0Þó§"õqUbíÄ€EæfÎ ¼ü~?8€_ͺ9CËÇgê~73ä߀Íü7áÛ€ƒãûk€mîý­€8?ï³R­:Šók€>ÿìç€é:?ÿþ~€~dÿ–ÿøS€¤9Ÿé|QþüâJ€º™jê>~ë•öÿ€^(þþñ/e€ÁÿþÆ~/‚$€¸½ÿ~–ÿÿüü€®)šÿðæ?ô þ})ƒ8¯ÿãcôÿô΃$€|‡4"î~3ƒ€>A3@ý"D€>_~éå€c€â?30~¦–€#€^ª}C¯34~úˆ€ì8ªKªƒ#úªó~úȀ爃Lc¯D~úÈ€êƒSƒ4D~úø€ï¿~ì-…ž@ícûú/ÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_BR/roms/cbios_main_msx2_br.rom0000644000175000017500000010000012262345041026100 0ustar manuelmanuel00000000000000óà ¿˜˜Ã’ÃÒ#äÃ$ÃÀÃG$ÃÆÆ$ÃÌ!ÃÞÚÃóÃýÃÃ"Ã.ÃQÃWÃ_ÃqÃ…äÃÃÃâÃëÃÃ5ÔÃæïøÃÃ?ÃrÃÈÃÛÃåÃðÃîÃÃÃÃ!Ã4ÃYÃ÷ÃÅÃêÃÃÿÃÙÃõÃà Ã)ÃÃÃ/ÃAÃSÃCöÃûà ÃfÃyËÃðÃÃÃÕÃÃ-ÃPÃbÃsÂÃ’ãõÃÅà à à Ã2 ÃC ÃS Ãe Ãw É Ú Ã[ÃÃÃ!Ã$Ã'Ã6Ã:Ã>ÃgÃyÃ}ÓÃ&ÃïÃõÃÞÃ,Ã=Ã2ÃGÃQÃÓO þ#(##øÉN#fié:àóæ¿GÍ.É:àóö@GÍ.Éó˹xÓ™yö€Ó™ûå!ßóyþ8þ0!ßÿx wáÉÍ_Û˜ÉõÍqñÓ˜Éó¯Ó™>ŽÓ™}Ó™|æ?Ó™ûÉó¯Ó™>ŽÓ™}Ó™|æ?ö@Ó™ûÉõ:¯üþ0ÍqÍQ xAO ñÓ˜ü ùÉ:¯üþ0Í_ÍGåë xA<˜í²= ûáÉë:¯üþ0ÍqÍQë xA<˜í³= ûëÉÝåÝ!ÑÃï:¯üþ(:=õ:éóæðo:ëóµGÍ.ñÀ:éóæð!êó¶*¿ó õÍqñÓ˜õ x± ÷ñÉ:ëóGÃ.:¯ü·ÈÍ_:¯üþ8*(ù%%:éóæÍ2*&ù¯Í2É:¯üþ8ÙÑ*(ùÍQ:éóW ó{Ó˜>Ó˜yÓ˜ Íå0 zÓ˜èûÉÝåÝ!ÕÃïÝåÝ!ÙÃïÝåÝ!ÝÃïÝåÝ!áÃï:°óþ)0(:ßóæñGÍ.:àóæçöG Í.³ó¯Í£ ¯Í£É:ßóæñöGÍ.:àóæçöG Í."ù>Í£ ¯Í£É:ßóæñGÍ.:àóæçG Í.½ó¯Í£¯Í£¯Í£¯Í£¯Í£É:ßóæñöGÍ.:àóæçG Í.Çó¯Í£>Í£>Í£¯Í£¯Í£É:ßóæñGÍ.:àóæçöG Í.Ñó¯Í£¯Í£¯Í£¯Í£¯Í£ÉÕõ!Á Fë~#fo)üGñ°GÍ.Ñ É  &o)))Íå0))í[&ùɇ‡*(ù_É:àó>Ð> Éõþ 8 :¯üþ(+þ0ñÉþ úñåÅ*¹ü "¹ü!"·üÁáÉñÝåÝ!‰ÃïÉñåÕÅõÍ.í[¹üíK·üÍÅ:éó2òó**ùíKËó @ü:¹üæO Ítð :¹ü/æOÍt*·ü "·üñÁÑáÉ:·üæõÅÕåÍý:·üæ(:,ù/2,ùá ÑÁñÍý:,ù/2,ùÉÁ ÁÁñÉõåÅÕWXíKÉó O:òóæGÍQõæ¸(ñõæ¸ ñÑÁáñÉñÑÁáñ/Éz³þÿ(ñËËËËæ°ÍWÝñæð°ÍWÚAŽÓ™á}Ó™ÉÍGÛ˜ÉõÍQñÓ˜ÉóÓ™>Ó™Û™õ¯Ó™>Ó™ûñÉÛ™Í.àÍ.Í.€Í.Í.Í.>2Üó2ÝóÍZ>!Í…>õ!  Í…õÍ.!¿ÍÃÉÍ.;Í.Í.É!¿í[$ùÃÃÀ:¯üþ Ðå!9ÍáÉKZvŒv °ÃÓ:°óþ(À8€*"ù> Í…>!²ûw³ûí°Ãê¯*$ùoÅÍ…Á:êó*ÉóÃ…:êóæG°*$ùÃ…:êóæG°!):êóæG°°!:êóæG°!:êó!õÍ%ñ,Í(Í!Ô*Í!$Í:öúg.&Í>-Í>À.ÍÍ%É}Í |óÓ™yö€Ó™ûÉ>Í›8øÉíK ùo&))) @üÅÕå:ùÍÒ#áÑÁ#ïÉåõ![Í« ñáÉRIGHTCåõ!mÍ« ñáÉLEFTCåõ!~Í« ñáÉUPCåõ!Í« ñáÉTUPCåõ!Í« ñáÉDOWNCåõ!®Í« ñáÉTDOWNCíK·üí[¹üÉSCALXYÅíC·üíS¹ü*¹ü))))).>ÿ2,ùyæ( G>ÿ§ü2,ùyæøO "*ùÁÉMAPXY:,ù**ùÉFETCHCåõ! Í« ñáÉSTORECåõ!+ Í« ñáÉSETATRåõ!= Í« ñáÉREADCåõ!N Í« ñáÉSETCåõ!^ Í« ñáÉNSETCXåõ!p Í« ñáÉGTASPCåõ!‚ Í« ñáÉPNTINIåõ!” Í« ñáÉSCANRåõ!¥ Í« ñáÉSCANL>#Ó.Í· >Ó.É~#·ÈÓ/øæþ 0Æ0Ó/ÉÆ7Ó/ÉõÍ¿ ñÍ¿ É|ÍÏ }ÍÏ ÉÉÝáýáñÁÑáÙñÁÑáûÉ>‚Ó«>PÓª¯Óÿ<Óþ<Óý<Óü!ÿÿÙÛ¨öðGxÓ¨:ÿÿ/öðOy2ÿÿ!ÿ>w¾ /w¾ %ò$|·(Ù¼8(Ù .gÙxÙGÙyÙOÙyÖO0ËxÖG0»Ù}·(Õ%ÃNxÓ¨y2ÿÿÙ!óùÍSÍ—ÍÞͯûÍóO!€ÅåÕ:ÁüÍÒ#áÑÁ¾ ë#ìÝ!€ý*ÀüÍG$>2êó2ëóÍ!i%Í3ó>Ó™>ŽÓ™¯Ó™ö@Ó™>vÓ˜¯Ó™!üúÓ™Û˜þv(ËÎËÖ¯Ó™>ŽÓ™ûxÍ/Í¿#>2ëó>2êó>2éó>2¯óÍÝ!AÍõ!i%Í3Í^ÍÚþÍ=>2™ý¯2)ûÍËþ!&Í3ÃC-BIOS Logo ROM!ÉüÊü?6í°!Áü¯å¶!@Í¥̲!€Í¥̲Ë(ÆËg(äá#<æ ÛÉGÍÒ##õxÍÒ##Wñ_xÉåÍ•!ABÍÆxáÉåõ!%Í3ñõGæÆ0ÍYxËx(>.ÍYxæÆ0ÍY> ÍY> ÍYñá##Í4(OÕÝáõýáÛ™·úòõåÍG$óáñÍ4(ËéÍ4(ËñÍ4(ËùGæ _xæ0³_|æ³!Éü_qxÉÅÍ•z³xÁÉ!Éü@~Ë(å!°%Í3á#ðÉ>!€ówó} í°>É!ówóí°>É!šýw›ýMí°>ÿ!ÚûwÛûí°>!ðûwñû'í°>!çÿwèÿí°>2çÿ>2èÿ!ðû"øó"úóÙ"HüÙ!€ó"Jü"tö!N%€óí°!"³ó!"·ó!"½ó! "¿ó!"Áó!"Ãó!8"Åó!"Çó! "Éó!"Ëó!"Íó!8"Ïó!"Ñó!"Õó!"×ó!8"Ùó!Yù"óó!uù"]ù!õù"cù!uú"iù>2\ù2bù2hù>'2®ó> 2¯ó:¯ó2°ó>2±ó>2ëó>2êó>2éó> 2àó:Áü2ù*" ù>Ã!Y"åþ2äþÉó!ÁüÛ¨Wæ?OÓ¨:ÿÿ/_æ2ÿÿG:ÿÿ/¸ {æöP2ÿÿG:ÿÿ/¸ €{{/2ÿÿzÓ¨p#yÆ@O0ÅûÉ!ÁüÅåy¶Ë Í ÍáÁØ# é¯2øúÉÅÍ ÁØÆöÉO2øú!Í&þC #Í&þDy7È·ÉyÅåÍÒ#áÁÉvýÉ:¯üþ0 ~·ÈÍY#÷~·ÈÝ!‰Íõ#ó :¯ü·À(:°óþ)ØPÉÅõÍP:°óíD<Ë?o:Ýó=…o&D:Üó=Ë?0 Ë!Ë· ôíK"ù ñÁÉåõ!Í« ñáÉSYNCHRÍHÿ~#þÈþ:Èþ08þ:Øþ (íþ (é·ÉõÍäþñÉ|ºÀ}»Éåõ!×Í« ñáÉGETYPRÙá~#^#V#ÕÝáõýáåÙÃG$>ÍÃîåõ!Í« ñáÉINIFNKåõ!Í« ñáÉSTRTMSûåÕ*úóí[øóç>ÿ ¯ÑáÉÍÂýåÕ*úóí[øóç ûvò~õ#}þ !ðû"úóñÑáÉåÕÅõͤý:¯üþ0ñõ͸ñõÍÍÍ:Ýó2aöñÁÑáÉÍÅÐ(õ:§ü·Âbñþ 8*þʪÍbÍW*Üó:°ó$¼8"Üóɱû&¯w*ÜóÍÃÓ !<Ã> Í—:Ýóæþ òÉ*Üó:±ó,½0 åÍêÍ5á-"ÜóÉ!"ÜóÉÍÓ*ÜóåÍ:Üó!±ó¾0ÍÓÍÓïá"ÜóÉ>2ÝóÉ>ÿ2§üÉ:Ýó!°ó¾0<:Üó!±ó¾Ð<2ÜóÚ:Ýó= :Üó=È2Üó:°ó2ÝóÉ:Üó=È2ÜóÉ:Üó!±ó¾Ð<2ÜóÉG< 2§üñ!`Ãñþ4(þ5(.y2ªü(y2©ü"2Ýó2Üó> > >>¯2§üÉ:Ýó!°ó¾Ð<2ÝóÉ:Ýó=È2ÝóÉÍ!±û:Üó_w:°ó ÍbÃ…Í*ÜóåE:±ó2ÜóG:ÜóÍbë=2ÜóÍbÍsðá"Üó&:±óW•=O!±ûëbk+í¸ÃÓÍ*ÜóåE:±óG:ÜóÍbë<2ÜóÍbÍsðÍÓá"Üó&:±ó•=O±ûT]#í°ÉõÅ:°óOþ)8 (Í:°óÖ(OÍÁñÉåÕÅüͤÁÑÕÅ!üÍÃÁá ëá É:ÝóþÈ> Í—Ã7:©üþÀ:¯üþÐ:ÌûÍbÃW:©üþÀ:¯üþÐÍbÍQ2Ìû§_ËËËËË˯!¯ü¾ *·ó*Áóåüͤ:ªüþ !ü!ü~/w#úáøë!üÍÃÍb>ÿÃW 7 Ä Ó ê * 7LUj*E*KÓJñlÐLñM5Y±ALBUCºDÇHêx©y­ͶÿõÍÙ8Í·(öñ > ͪ¯2ôñ7ÉõÓ‘>Ó/Óñ§ÉÍ»ÿÛ>ÿ0/§Éåõ!¦ü¯¾w ñþ wñþ@8 þ`0Ö@¿þP7áÉÍÛý:ªö§Ê>2ÝóÃ? Íàý!üÍ3*Ýó"Êû°û&}wÍ4þʘþ 0!™Í¯2¨ü2ªüãõ:¨ü§Ä?ñßÉ͸*Üó"¼ö> 2§öÍbÍQþ (*Üó:°ó¼ &±û~·(:§öÍbÍW*¼ö"ÜóÉÃÍ> õ:§ößñ2§öÃÉ7áÉÉÉÉɯáÉÉÉÉÉŠ‹Ž‘’•–—ÛªæðöÓªÛ©æÀÛªæðöÓªÛ©æÀ7Éåõ!Í« ñáÉISCNTCÃõåõ!Í« ñáÉBEEP"ÜóÉåõ!)Í« ñáÉFNKSBåõ!:Í« ñáÉERAFNKåõ!LÍ« ñáÉDSPFNK:¯üþØ:°üͽý·Ê”Ãåõ!rÍ« ñá7ÉTAPIONåõ!…Í« ñá7ÉTAPINåõ!–Í« ñáÉTAPIOFåõ!©Í« ñá7ÉTAPOONåõ!¼Í« ñá7ÉTAPOUTåõ!ÎÍ« ñáÉTAPOOFÅGÛª( Ëç( ˧(ÁÉîÓªÁÉ>Í<Í<Í<¸>ÍÉóÓ õ{Ó¡ûñÉӠۢɷ>(<Ó«ÉÛ¨ÉÓ¨ÉÛ™ÉóÅOÛªæð±ÓªÛ©ÁûÉͧÿÉͬÿÉ:dø§ÉÅþ >Í'/æå!¦_~áÁ§ÉåÕ=(Ëó>óÍû濳_>Í>óÍû/æ!–O ~ÑáÁ§É>Á§Éþ0· >Í'öþ<Éó=ÕGæ(L>Í濳_>Íxæ( >óÍûÑ (>ÿɯÉåõ!Í« ñá¯ÉGTPADåõ!Í« ñáÉGTPDLÍNF#~##¦o&ÉÍN~2öó¯Í¶/æ2èóÍ*øóí[úóç :÷ó=2÷ó !Úûÿ q#üÍ>2÷óÝáýáñÁÑáÙñÁÑáûÉÍÖýíEÛªæðO !åûyÓªÛ©w# öÝ!Úûåû:ëû0!ž&!Î&ÍJ!¾'ݾÄj/ݦÝw8# ùÝ#áÉõ>2÷óñÉõxþ(þ(ÃÉyþ >!þ >þ 5>yþ >þ $>åÅÕ!ø_ë§(ÕÍÖÑôÑÁá ~§(åÍÖáñÃ`*øów#}þ !ðûÕí[úóçÑÈ"øóÉÍõÝáÉÙõÅÕåíWõÙýå:øúõýáÍG$ýáÙñâûáÑÁñÙÉþÍÚþ&ÃNåõ!6Í« ñáç%ÃNCALBASí[ÜóÕ"ÜóÍÓÑíSÜóÉÛ™!æ' ™í³¯Ó˜ x± ø!¿~Ó˜# x± ÷>Ó™>@Ó™!Î%~Ó˜#~§ ùÓ˜§ ùÃüÍ $_EñæÍ $GÛ¨W£°áÍ€ó{ÑõËzÄ-%ñáÁÉÈüÉåWÕ§óüñ$ÁáÕå|æoG>üÍ $_EzæÍ $GÛ¨W£°áYÍ…óÑåËzÄ-%áÉÙóýåñÝåáW§üñ$ÕzæGüÝåñæ‡(ËË= ù!z$åÛ¨õ¡°ÙÃŒóÙóÑËzÄ-%ÙÉóåoæG>«ÆUòŽ$W|ægG>Àòž$_/Oz£G}§òé$æåÅG>«ÆUò¸$£GzæÀgÛ¨oæÀ´Ó¨:ÿÿ/¡°O2ÿÿ}Ó¨!Åüzæ…o|ÎgywÁáÛ¨¡°Ó¨áÉW_æÀoÛ¨Oæ?µÓ¨{æo|&))Ö@0ú|/g:ÿÿ/_¤µ2ÿÿoyÓ¨zæO}!Åü wÉzæÀGÛ¨Oæ?°Ó¨{2ÿÿyÓ¨zæO!Åü sÉÓ¨^Ó¨szÓ¨ÉÓ¨͘óñÓ¨ÉÝéC-BIOS 0.25 cbios.sf.net Localization: BR Init ROM in slot: Cannot execute a BASIC ROM. ERROR:MEMORY NOT FOUND.CALLED NON EXISTING BASIC.STACK ERROR. No cartridge found. This version of C-BIOS can only start cartridges. Please restart your MSX (emulator) with a cartridge inserted.0123456789-=\[];'`,./abcdefghijklmnopqrstuvwxyz)!@#$%^&*(_+|{}:"~<>?ABCDEFGHIJKLMNOPQRSTUVWXYZ ¬«ºï½ôûìñ »óòļÇÍÜÆÝÈ ÂÛÌÒÀÏ ýüõð÷®¯öþúÁÎÔÖßÊÞÉ ÓÃ×Ë©ÑÅÕÐùªøëŸÙ¿›˜àáç‡îéíÚ·¹å†¦§„—‹Œ”±¡‘³µæ¤¢£ƒ“‰–‚•ˆŠ …Ø­ž¾œâ€è궸䨎™š°’²´¥ã  *+/0123456789-,.€p‚„õ‡@ÅåÁ{²( øÁÉåõ!µ1Í« > Ó.zÓ/{Ó/ñáÉROMBAS>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:>:åõ!I:Í« ñáÉBASIC statements are not implemented yetÍ3Éåõ!"}Í« ñáÉunknown@7D179}Ý!‰ÍõÉÝ!…Ãõåõ!~Í« ñáÉunknown@7E14Éopenmsx-0.10.0/Contrib/cbios/C-BIOS_MSX2_BR/hardwareconfig.xml0000644000175000017500000000472612262345041024371 0ustar manuelmanuel00000000000000 C-BIOS MSX2 BR 2010 An MSX2 machine using C-BIOS, with Brazillian settings, like 60Hz interrupt frequency. MSX2 largest a197a7f78900fd9a168d8e726c3651706357671e roms/cbios_main_msx2_br.rom 2fcb40413e7d373f0f2dbdc815ce18746ddf3684 roms/cbios_sub.rom 512 16000 false int true false false V9938 128 YM2149 21000 cbios-msx2.cmos openmsx-0.10.0/Contrib/openmsx-control-stdio.cc0000644000175000017500000002273012262345041022246 0ustar manuelmanuel00000000000000/** * Example implementation for bidirectional communication with openMSX. * * requires: libxml2 * compile: g++ `xml2-config --cflags` `xml2-config --libs` openmsx-control.cc */ #include #include #include #include #include #include #include using std::cout; using std::endl; using std::list; using std::string; class OpenMSXComm { public: // main loop void start(); // send a command to openmsx void sendCommand(const string& command); private: // therse methods get called when openMSX has send the corresponding tag void openmsx_cmd_ok(const string& msg); void openmsx_cmd_nok(const string& msg); void openmsx_cmd_info(const string& msg); void openmsx_cmd_warning(const string& msg); void openmsx_cmd_update(const string& name, const string& value); // XML parsing call-back functions static void cb_start_element(OpenMSXComm* comm, const xmlChar* name, const xmlChar** attrs); static void cb_end_element(OpenMSXComm* comm, const xmlChar* name); static void cb_text(OpenMSXComm* comm, const xmlChar* chars, int len); void parseReply(const char** attrs); void parseLog(const char** attrs); void parseUpdate(const char** attrs); void doReply(); void doLog(); void doUpdate(); void deprecated(); // commands being executed list commandStack; // XML parsing enum State { START, TAG_OPENMSX, TAG_REPLY, TAG_LOG, TAG_UPDATE, TAG_OK, TAG_NOK, TAG_INFO, TAG_WARNING, } state; unsigned unknownLevel; string content; xmlSAXHandler sax_handler; xmlParserCtxt* parser_context; enum ReplyStatus { REPLY_UNKNOWN, REPLY_OK, REPLY_NOK } replyStatus; enum LogLevel { LOG_UNKNOWN, LOG_INFO, LOG_WARNING } logLevel; enum UpdateType { UPDATE_UNKNOWN, UPDATE_LED } updateType; string updateName; // communication with openmsx process int fd_out; }; void OpenMSXComm::openmsx_cmd_ok(const string& msg) { const string& command = commandStack.front(); cout << "CMD: '" << command << "' executed" << endl; if (!msg.empty()) { cout << msg << endl; } commandStack.pop_front(); } void OpenMSXComm::openmsx_cmd_nok(const string& msg) { const string& command = commandStack.front(); cout << "CMD: '" << command << "' failed" << endl; if (!msg.empty()) { cout << msg << endl; } commandStack.pop_front(); } void OpenMSXComm::openmsx_cmd_info(const string& msg) { cout << "INFO: " << msg << endl; } void OpenMSXComm::openmsx_cmd_warning(const string& msg) { cout << "WARNING: " << msg << endl; } void OpenMSXComm::openmsx_cmd_update(const string& name, const string& value) { cout << "UPDATE: " << name << " " << value << endl; } void OpenMSXComm::deprecated() { static bool alreadyPrinted = false; if (!alreadyPrinted) { alreadyPrinted = true; cout << "The openMSX you're running still uses the old communication protocol." << endl << "Because of this some stuff will not work (LED status for example)." << endl << "Please upgrade to openMSX 0.4.0 or higher." << endl; } } void OpenMSXComm::cb_start_element(OpenMSXComm* comm, const xmlChar* name, const xmlChar** attrs) { if (comm->unknownLevel) { ++(comm->unknownLevel); return; } switch (comm->state) { case START: if (strcmp((const char*)name, "openmsx-output") == 0) { comm->state = TAG_OPENMSX; } else { ++(comm->unknownLevel); } break; case TAG_OPENMSX: if (strcmp((const char*)name, "reply") == 0) { comm->state = TAG_REPLY; comm->parseReply((const char**)attrs); } else if (strcmp((const char*)name, "log") == 0) { comm->state = TAG_LOG; comm->parseLog((const char**)attrs); } else if (strcmp((const char*)name, "update") == 0) { comm->state = TAG_UPDATE; comm->parseUpdate((const char**)attrs); } // backwards compatibilty stuff else if (strcmp((const char*)name, "ok") == 0) { comm->state = TAG_OK; comm->replyStatus = REPLY_OK; comm->deprecated(); } else if (strcmp((const char*)name, "nok") == 0) { comm->state = TAG_NOK; comm->replyStatus = REPLY_NOK; comm->deprecated(); } else if (strcmp((const char*)name, "info") == 0) { comm->state = TAG_INFO; comm->logLevel = LOG_INFO; comm->deprecated(); } else if (strcmp((const char*)name, "warning") == 0) { comm->state = TAG_WARNING; comm->logLevel = LOG_WARNING; comm->deprecated(); } else { ++(comm->unknownLevel); } break; default: ++(comm->unknownLevel); break; } comm->content.clear(); } void OpenMSXComm::parseReply(const char** attrs) { replyStatus = REPLY_UNKNOWN; if (attrs) { for ( ; *attrs; attrs += 2) { if (strcmp(attrs[0], "result") == 0) { if (strcmp(attrs[1], "ok") == 0) { replyStatus = REPLY_OK; } else if (strcmp(attrs[1], "nok") == 0) { replyStatus = REPLY_NOK; } } } } } void OpenMSXComm::parseLog(const char** attrs) { logLevel = LOG_UNKNOWN; if (attrs) { for ( ; *attrs; attrs += 2) { if (strcmp(attrs[0], "level") == 0) { if (strcmp(attrs[1], "info") == 0) { logLevel = LOG_INFO; } else if (strcmp(attrs[1], "warning") == 0) { logLevel = LOG_WARNING; } } } } } void OpenMSXComm::parseUpdate(const char** attrs) { updateType = UPDATE_UNKNOWN; if (attrs) { for ( ; *attrs; attrs += 2) { if (strcmp(attrs[0], "type") == 0) { if (strcmp(attrs[1], "led") == 0) { updateType = UPDATE_LED; } } else if (strcmp(attrs[0], "name") == 0) { updateName = attrs[1]; } } } } void OpenMSXComm::cb_end_element(OpenMSXComm* comm, const xmlChar* name) { if (comm->unknownLevel) { --(comm->unknownLevel); return; } switch (comm->state) { case TAG_OPENMSX: comm->state = START; break; case TAG_REPLY: comm->doReply(); comm->state = TAG_OPENMSX; break; case TAG_LOG: comm->doLog(); comm->state = TAG_OPENMSX; break; case TAG_UPDATE: comm->doUpdate(); comm->state = TAG_OPENMSX; break; // backwards compatibilty stuff case TAG_OK: comm->openmsx_cmd_ok(comm->content); comm->state = TAG_OPENMSX; break; case TAG_NOK: comm->openmsx_cmd_nok(comm->content); comm->state = TAG_OPENMSX; break; case TAG_INFO: comm->openmsx_cmd_info(comm->content); comm->state = TAG_OPENMSX; break; case TAG_WARNING: comm->openmsx_cmd_warning(comm->content); comm->state = TAG_OPENMSX; break; default: break; } } void OpenMSXComm::doReply() { switch (replyStatus) { case REPLY_OK: openmsx_cmd_ok(content); break; case REPLY_NOK: openmsx_cmd_nok(content); break; } } void OpenMSXComm::doLog() { switch (logLevel) { case LOG_INFO: openmsx_cmd_info(content); break; case LOG_WARNING: openmsx_cmd_warning(content); break; } } void OpenMSXComm::doUpdate() { switch (updateType) { case UPDATE_LED: openmsx_cmd_update(updateName, content); break; } } void OpenMSXComm::cb_text(OpenMSXComm* comm, const xmlChar* chars, int len) { switch (comm->state) { case TAG_REPLY: case TAG_LOG: case TAG_UPDATE: case TAG_OK: case TAG_NOK: case TAG_INFO: case TAG_WARNING: comm->content.append((const char*)chars, len); break; default: break; } } void OpenMSXComm::sendCommand(const string& command) { write(fd_out, "", 9); write(fd_out, command.c_str(), command.length()); write(fd_out, "", 10); commandStack.push_back(command); } void OpenMSXComm::start() { // init XML parser state = START; unknownLevel = 0; memset(&sax_handler, 0, sizeof(sax_handler)); sax_handler.startElement = (startElementSAXFunc)cb_start_element; sax_handler.endElement = (endElementSAXFunc) cb_end_element; sax_handler.characters = (charactersSAXFunc) cb_text; parser_context = xmlCreatePushParserCtxt(&sax_handler, this, 0, 0, 0); // create pipes int pipe_to_child[2]; int pipe_from_child[2]; pipe(pipe_to_child); pipe(pipe_from_child); fd_out = pipe_to_child[1]; // start openmsx sub-process pid_t pid = fork(); if (pid == 0) { dup2(pipe_to_child[0], STDIN_FILENO); dup2(pipe_from_child[1], STDOUT_FILENO); close(pipe_to_child[0]); close(pipe_to_child[1]); close(pipe_from_child[0]); close(pipe_from_child[1]); execlp("openmsx", "openmsx", "-control", "stdio:", 0); exit(0); } close(pipe_to_child[0]); close(pipe_from_child[1]); // send initial commands write(pipe_to_child[1], "", 17); sendCommand("set power on"); sendCommand("restoredefault renderer"); // event loop string command; // (partial) input from STDIN while (true) { char buf[4096]; fd_set rdfs; FD_ZERO(&rdfs); FD_SET(pipe_from_child[0], &rdfs); FD_SET(STDIN_FILENO, &rdfs); select(pipe_from_child[0] + 1, &rdfs, NULL, NULL, NULL); if (FD_ISSET(pipe_from_child[0], &rdfs)) { // data available from openMSX ssize_t size = read(pipe_from_child[0], buf, 4096); if (size == 0) { // openmsx process died break; } xmlParseChunk(parser_context, buf, size, 0); } if (FD_ISSET(STDIN_FILENO, &rdfs)) { // data available from STDIN ssize_t size = read(STDIN_FILENO, buf, 4096); char* oldpos = buf; while (true) { char* pos = (char*)memchr(oldpos, '\n', size); if (pos) { unsigned num = pos - oldpos; command.append(oldpos, num); sendCommand(command); command.clear(); oldpos = pos + 1; size -= num + 1; } else { command.append(pos, size); break; } } } } // cleanup xmlFreeParserCtxt(parser_context); } int main() { OpenMSXComm comm; comm.start(); return 0; } openmsx-0.10.0/Contrib/openmsx-control-socket.cc0000644000175000017500000002434712262345041022422 0ustar manuelmanuel00000000000000/** * Example implementation for bidirectional communication with openMSX. * * requires: libxml2 * compile: * *nix: g++ `xml2-config --cflags` `xml2-config --libs` openmsx-control-socket.cc * win32: g++ `xml2-config --cflags` `xml2-config --libs` openmsx-control-socket.cc -lwsock32 */ #include #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #else #include #include #include #include #endif using std::cout; using std::endl; using std::deque; using std::string; using std::vector; class ReadDir { public: ReadDir(const std::string& directory) { dir = opendir(directory.c_str()); } ~ReadDir() { if (dir) { closedir(dir); } } dirent* getEntry() { if (!dir) { return 0; } return readdir(dir); } private: DIR* dir; }; static string getTempDir() { const char* result = NULL; if (!result) result = getenv("TMPDIR"); if (!result) result = getenv("TMP"); if (!result) result = getenv("TEMP"); if (!result) { #ifdef _WIN32 result = "C:/WINDOWS/TEMP"; #else result = "/tmp"; #endif } return result; } static string getUserName() { #ifdef _WIN32 return "default"; #else struct passwd* pw = getpwuid(getuid()); return pw->pw_name ? pw->pw_name : ""; #endif } class OpenMSXComm { public: // main loop void start(int sd); // send a command to openmsx void sendCommand(const string& command); private: // XML parsing call-back functions static void cb_start_element(OpenMSXComm* comm, const xmlChar* name, const xmlChar** attrs); static void cb_end_element(OpenMSXComm* comm, const xmlChar* name); static void cb_text(OpenMSXComm* comm, const xmlChar* chars, int len); void parseReply(const char** attrs); void parseLog(const char** attrs); void parseUpdate(const char** attrs); void doReply(); void doLog(); void doUpdate(); // commands being executed deque commandStack; // XML parsing enum State { START, TAG_OPENMSX, TAG_REPLY, TAG_LOG, TAG_UPDATE, } state; unsigned unknownLevel; string content; xmlSAXHandler sax_handler; xmlParserCtxt* parser_context; enum ReplyStatus { REPLY_UNKNOWN, REPLY_OK, REPLY_NOK } replyStatus; enum LogLevel { LOG_UNKNOWN, LOG_INFO, LOG_WARNING } logLevel; string updateType; string updateName; // communication with openmsx process int sd; }; void OpenMSXComm::cb_start_element(OpenMSXComm* comm, const xmlChar* name, const xmlChar** attrs) { if (comm->unknownLevel) { ++(comm->unknownLevel); return; } switch (comm->state) { case START: if (strcmp((const char*)name, "openmsx-output") == 0) { comm->state = TAG_OPENMSX; } else { ++(comm->unknownLevel); } break; case TAG_OPENMSX: if (strcmp((const char*)name, "reply") == 0) { comm->state = TAG_REPLY; comm->parseReply((const char**)attrs); } else if (strcmp((const char*)name, "log") == 0) { comm->state = TAG_LOG; comm->parseLog((const char**)attrs); } else if (strcmp((const char*)name, "update") == 0) { comm->state = TAG_UPDATE; comm->parseUpdate((const char**)attrs); } else { ++(comm->unknownLevel); } break; default: ++(comm->unknownLevel); break; } comm->content.clear(); } void OpenMSXComm::parseReply(const char** attrs) { replyStatus = REPLY_UNKNOWN; if (attrs) { for ( ; *attrs; attrs += 2) { if (strcmp(attrs[0], "result") == 0) { if (strcmp(attrs[1], "ok") == 0) { replyStatus = REPLY_OK; } else if (strcmp(attrs[1], "nok") == 0) { replyStatus = REPLY_NOK; } } } } } void OpenMSXComm::parseLog(const char** attrs) { logLevel = LOG_UNKNOWN; if (attrs) { for ( ; *attrs; attrs += 2) { if (strcmp(attrs[0], "level") == 0) { if (strcmp(attrs[1], "info") == 0) { logLevel = LOG_INFO; } else if (strcmp(attrs[1], "warning") == 0) { logLevel = LOG_WARNING; } } } } } void OpenMSXComm::parseUpdate(const char** attrs) { updateType = "unknown"; if (attrs) { for ( ; *attrs; attrs += 2) { if (strcmp(attrs[0], "type") == 0) { updateType = attrs[1]; } else if (strcmp(attrs[0], "name") == 0) { updateName = attrs[1]; } } } } void OpenMSXComm::cb_end_element(OpenMSXComm* comm, const xmlChar* name) { if (comm->unknownLevel) { --(comm->unknownLevel); return; } switch (comm->state) { case TAG_OPENMSX: comm->state = START; break; case TAG_REPLY: comm->doReply(); comm->state = TAG_OPENMSX; break; case TAG_LOG: comm->doLog(); comm->state = TAG_OPENMSX; break; case TAG_UPDATE: comm->doUpdate(); comm->state = TAG_OPENMSX; break; default: break; } } void OpenMSXComm::doReply() { switch (replyStatus) { case REPLY_OK: cout << "OK: "; break; case REPLY_NOK: cout << "ERR: "; break; } cout << commandStack.front() << endl; commandStack.pop_front(); if (!content.empty()) { cout << content << endl; } } void OpenMSXComm::doLog() { switch (logLevel) { case LOG_INFO: cout << "INFO: "; break; case LOG_WARNING: cout << "WARNING: "; break; } cout << content << endl; } void OpenMSXComm::doUpdate() { cout << "UPDATE: " << updateType << " " << updateName << " " << content << endl; } void OpenMSXComm::cb_text(OpenMSXComm* comm, const xmlChar* chars, int len) { switch (comm->state) { case TAG_REPLY: case TAG_LOG: case TAG_UPDATE: comm->content.append((const char*)chars, len); break; default: break; } } void OpenMSXComm::sendCommand(const string& command) { write(sd, "", 9); write(sd, command.c_str(), command.length()); write(sd, "", 10); commandStack.push_back(command); } void OpenMSXComm::start(int sd_) { sd = sd_; // init XML parser state = START; unknownLevel = 0; memset(&sax_handler, 0, sizeof(sax_handler)); sax_handler.startElement = (startElementSAXFunc)cb_start_element; sax_handler.endElement = (endElementSAXFunc) cb_end_element; sax_handler.characters = (charactersSAXFunc) cb_text; parser_context = xmlCreatePushParserCtxt(&sax_handler, this, 0, 0, 0); write(sd, "", 17); // event loop string command; // (partial) input from STDIN while (true) { char buf[4096]; fd_set rdfs; FD_ZERO(&rdfs); FD_SET(sd, &rdfs); FD_SET(STDIN_FILENO, &rdfs); select(sd + 1, &rdfs, NULL, NULL, NULL); if (FD_ISSET(sd, &rdfs)) { // data available from openMSX ssize_t size = read(sd, buf, 4096); if (size == 0) { // openmsx process died break; } xmlParseChunk(parser_context, buf, size, 0); } if (FD_ISSET(STDIN_FILENO, &rdfs)) { // data available from STDIN ssize_t size = read(STDIN_FILENO, buf, 4096); char* oldpos = buf; while (true) { char* pos = (char*)memchr(oldpos, '\n', size); if (pos) { unsigned num = pos - oldpos; command.append(oldpos, num); sendCommand(command); command.clear(); oldpos = pos + 1; size -= num + 1; } else { command.append(pos, size); break; } } } } // cleanup xmlFreeParserCtxt(parser_context); } static bool checkSocketDir(const string& dir) { struct stat st; if (stat(dir.c_str(), &st)) { // cannot stat return false; } if (!S_ISDIR(st.st_mode)) { // not a directory return false; } #ifndef _WIN32 // only do permission and owner checks on *nix if ((st.st_mode & 0777) != 0700) { // wrong permissions return false; } if (st.st_uid != getuid()) { // wrong uid return false; } #endif return true; } static bool checkSocket(const string& socket) { string dir = socket.substr(0, socket.find_last_of('/')); string name = socket.substr(socket.find_last_of('/') + 1); if (name.substr(0, 7) != "socket.") { // wrong name return false; } struct stat st; if (stat(socket.c_str(), &st)) { // cannot stat return false; } #ifdef _WIN32 if (!S_ISREG(st.st_mode)) { // not a regular file return false; } #else if (!S_ISSOCK(st.st_mode)) { // not a socket return false; } #endif #ifndef _WIN32 // only do permission and owner checks on *nix if ((st.st_mode & 0777) != 0600) { // check will be different on win32 (!= 777) thus actually useless // wrong permissions return false; } if (st.st_uid != getuid()) { // does this work on win32? is this check meaningful? // wrong uid return false; } #endif return true; } static void deleteSocket(const string& socket) { unlink(socket.c_str()); // ignore errors string dir = socket.substr(0, socket.find_last_of('/')); rmdir(dir.c_str()); // ignore errors } static int openSocket(const string& socketName) { if (!checkSocket(socketName)) { return -1; } #ifdef _WIN32 int port = -1; std::ifstream in(socketName.c_str()); in >> port; if (port == -1) { return -1; } int sd = socket(AF_INET, SOCK_STREAM, 0); if (sd == -1) { return -1; } sockaddr_in addr; memset((char*)&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons(port); #else int sd = socket(AF_UNIX, SOCK_STREAM, 0); if (sd == -1) { return -1; } sockaddr_un addr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, socketName.c_str()); #endif if (connect(sd, (sockaddr*)&addr, sizeof(addr)) == -1) { // It appears to be a socket but we cannot connect to it. // Must be a stale socket. Try to clean it up. deleteSocket(socketName); close(sd); return -1; } return sd; } void collectServers(vector& servers) { string dir = getTempDir() + "/openmsx-" + getUserName(); if (!checkSocketDir(dir)) { return; } ReadDir readDir(dir); while (dirent* entry = readDir.getEntry()) { string socketName = dir + '/' + entry->d_name; int sd = openSocket(socketName); if (sd != -1) { close(sd); servers.push_back(socketName); } } } int main() { #ifdef _WIN32 WSAData wsaData; WSAStartup(MAKEWORD(1, 1), &wsaData); #endif vector servers; collectServers(servers); if (servers.empty()) { cout << "No running openmsx found." << endl; return 0; } // TODO let the user pick one if there is more than 1 int sd = openSocket(servers.front()); OpenMSXComm comm; comm.start(sd); return 0; } openmsx-0.10.0/Contrib/README.openmsx-control0000644000175000017500000000105612262345041021474 0ustar manuelmanuel00000000000000openMSX Control Example ======================= Using the "-control" command line parameter, the openMSX process can be controlled from another process. The control protocol XML-based. In openmsx-control.cc you'll find an example client application which communicates with openMSX using the control protocol. Note: We try to keep the control protocol stable, but there is no hard guarantee it won't change in the next release. author: Wouter Vermaelen openmsx-control.cc is public domain, use it as you see fit. There is no warranty of any kind. openmsx-0.10.0/src/0000755000175000017500000000000012262345041014633 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/Schedulable.cc0000644000175000017500000000267712262345041017371 0ustar manuelmanuel00000000000000#include "Schedulable.hh" #include "Scheduler.hh" #include "serialize.hh" #include "serialize_stl.hh" #include namespace openmsx { Schedulable::Schedulable(Scheduler& scheduler_) : scheduler(scheduler_) { } Schedulable::~Schedulable() { removeSyncPoints(); } void Schedulable::schedulerDeleted() { std::cerr << "Internal error: Schedulable \"" << typeid(*this).name() << "\" failed to unregister." << std::endl; } void Schedulable::setSyncPoint(EmuTime::param timestamp, int userData) { scheduler.setSyncPoint(timestamp, *this, userData); } bool Schedulable::removeSyncPoint(int userData) { return scheduler.removeSyncPoint(*this, userData); } void Schedulable::removeSyncPoints() { scheduler.removeSyncPoints(*this); } bool Schedulable::pendingSyncPoint(int userData) const { return scheduler.pendingSyncPoint(*this, userData); } Scheduler& Schedulable::getScheduler() const { return scheduler; } EmuTime::param Schedulable::getCurrentTime() const { return scheduler.getCurrentTime(); } template void Schedulable::serialize(Archive& ar, unsigned /*version*/) { Scheduler::SyncPoints syncPoints; if (!ar.isLoader()) { syncPoints = scheduler.getSyncPoints(*this); } ar.serialize("syncPoints", syncPoints); if (ar.isLoader()) { removeSyncPoints(); for (auto& s : syncPoints) { setSyncPoint(s.getTime(), s.getUserData()); } } } INSTANTIATE_SERIALIZE_METHODS(Schedulable); } // namespace openmsx openmsx-0.10.0/src/Printer.cc0000644000175000017500000017622212262345041016577 0ustar manuelmanuel00000000000000#include "Printer.hh" #include "PNG.hh" #include "FileOperations.hh" #include "IntegerSetting.hh" #include "MSXMotherBoard.hh" #include "CliComm.hh" #include "MSXException.hh" #include "Math.hh" #include "MemBuffer.hh" #include "serialize.hh" #include "memory.hh" #include "vla.hh" #include #include #include #include #include using std::max; using std::min; using std::string; namespace openmsx { class Paper { public: Paper(unsigned x, unsigned y, double dotSizeX, double dotSizeY); ~Paper(); std::string save() const; void setDotSize(double sizeX, double sizeY); void plot(double x, double y); private: byte& dot(unsigned x, unsigned y); MemBuffer buf; std::vector table; double radiusX; double radiusY; int radius16; unsigned sizeX; unsigned sizeY; }; PrinterCore::PrinterCore() : toPrint(0), prevStrobe(true) { } PrinterCore::~PrinterCore() { } bool PrinterCore::getStatus(EmuTime::param /*time*/) { return false; // false = low = ready } void PrinterCore::setStrobe(bool strobe, EmuTime::param /*time*/) { if (!strobe && prevStrobe) { // falling edge write(toPrint); } prevStrobe = strobe; } void PrinterCore::writeData(byte data, EmuTime::param /*time*/) { toPrint = data; } void PrinterCore::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { // nothing } void PrinterCore::unplugHelper(EmuTime::param /*time*/) { forceFormFeed(); } /* // class RawPrinter RawPrinter::RawPrinter() { Properties* properties = propGetGlobalProperties(); hFile = CreateFile(properties->ports.Lpt.portName, GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH, nullptr); if (hFile == INVALID_HANDLE_VALUE) { throw MSXException(); } } void RawPrinter::~RawPrinter() { CloseHandle(hFile); } void RawPrinter::write(byte value) { unsigned dwWritten; WriteFile(hFile, &value, 1, &dwWritten, nullptr); } void RawPrinter::forceFormFeed() { } */ // class ImagePrinter ImagePrinter::ImagePrinter(MSXMotherBoard& motherBoard_, bool graphicsHiLo_) : motherBoard(motherBoard_) , graphicsHiLo(graphicsHiLo_) { auto& info = motherBoard.getSharedStuff("print-resolution"); if (info.counter == 0) { assert(!info.stuff); info.stuff = new IntegerSetting( motherBoard.getCommandController(), "print-resolution", "resolution of the output image of emulated dot matrix printer in DPI", 300, 72, 1200); } ++info.counter; dpiSetting = reinterpret_cast(info.stuff); letterQuality = false; bold = false; proportional = false; italic = false; superscript = false; subscript = false; doubleWidth = false; underline = false; doubleStrike = false; escSequence = false; alternateChar = false; detectPaperOut = false; japanese = false; normalAfterLine = false; ninePinGraphics = false; leftToRight = false; elite = false; compressed = false; noHighEscapeCodes = false; eightBit = 0; perforationSkip = 0; remainingCommandBytes = 0; sizeEscPos = 0; sizeRemainingDataBytes = 0; ramLoadOffset = 0; ramLoadEnd = 0; countryCode = CC_USA; printAreaTop = -1.0; printAreaBottom = 0.0; } ImagePrinter::~ImagePrinter() { flushEmulatedPrinter(); auto& info = motherBoard.getSharedStuff("print-resolution"); assert(info.counter); assert(dpiSetting); assert(dpiSetting == info.stuff); --info.counter; if (info.counter == 0) { delete dpiSetting; info.stuff = nullptr; } } void ImagePrinter::write(byte data) { if (ramLoadOffset < ramLoadEnd) { fontInfo.ram[ramLoadOffset++] = data; } else if (sizeRemainingDataBytes) { if (eightBit == 0) { data &= 0x80; } else if (eightBit == 1) { data |= 0x80; } printGraphicByte(data); sizeRemainingDataBytes--; } else if (escSequence) { escSequence = false; memset(&(abEscSeq), 0, sizeof(abEscSeq)); *(abEscSeq) = data; sizeEscPos = 1; remainingCommandBytes = calcEscSequenceLength(data); if (!remainingCommandBytes) { processEscSequence(); } } else if (remainingCommandBytes) { abEscSeq[sizeEscPos++] = data; if (!--remainingCommandBytes) { processEscSequence(); } } else { processCharacter(data); } } void ImagePrinter::forceFormFeed() { flushEmulatedPrinter(); } void ImagePrinter::resetEmulatedPrinter() { resetSettings(); pageHeight = pageTop + lines * lineFeed; hpos = leftBorder; vpos = pageTop; } void ImagePrinter::plot9Dots(double x, double y, unsigned pattern) { for (int i = 0; i < 9; ++i) { if (pattern & (1 << i)) { paper->plot(x, y + (8 - i) * pixelSizeY); } } } void ImagePrinter::printGraphicByte(byte data) { ensurePrintPage(); double destY = vpos * pixelSizeY; double destHeight = pixelSizeY * 9.0; // Print Data to high 8 bits unsigned charBits = (graphicsHiLo ? Math::reverseByte(data) : data) << 1; printAreaTop = min(printAreaTop, destY); printAreaBottom = max(printAreaBottom, destY + destHeight); // Print bit-mask plot9Dots(hpos * pixelSizeX, destY, charBits); // Move print-position... seekPrinterHeadRelative(1.0 / graphDensity); } void ImagePrinter::seekPrinterHeadRelative(double offset) { hpos += offset; if (unsigned(hpos) > rightBorder) { hpos = leftBorder; vpos += lineFeed; if (vpos >= pageHeight) { doubleWidth ^= normalAfterLine; flushEmulatedPrinter(); } } } void ImagePrinter::ensurePrintPage() { if (!paper) { // A4 paper format (210mm x 297mm) at 300dpi // TODO make this configurable int dpi = dpiSetting->getInt(); unsigned paperSizeX = unsigned((210 / 25.4) * dpi); unsigned paperSizeY = unsigned((297 / 25.4) * dpi); unsigned dotsX, dotsY; getNumberOfDots(dotsX, dotsY); pixelSizeX = double(paperSizeX) / dotsX; pixelSizeY = double(paperSizeY) / dotsY; paper = make_unique(paperSizeX, paperSizeY, pixelSizeX, pixelSizeY); } } void ImagePrinter::flushEmulatedPrinter() { if (paper) { if (printAreaBottom > printAreaTop) { try { string filename = paper->save(); motherBoard.getMSXCliComm().printInfo( "Printed to " + filename); } catch (MSXException& e) { motherBoard.getMSXCliComm().printWarning( "Failed to print: " + e.getMessage()); } printAreaTop = -1.0; printAreaBottom = 0.0; } paper.reset(); } hpos = leftBorder; vpos = pageTop; } static unsigned compress9(unsigned a) { unsigned result = 0; for (unsigned i = 0; i < 9; ++i) { if (a & (1 << i)) { result |= 1 << (i / 2); } } return result; } void ImagePrinter::printVisibleCharacter(byte data) { ensurePrintPage(); double iYPos = 0; byte* charBitmap = (fontInfo.useRam ? fontInfo.ram : fontInfo.rom) + fontInfo.charWidth * data; byte attribute = charBitmap[0]; unsigned start = (attribute >> 4) & 0x07; unsigned end = attribute & 0x0f; unsigned topBits = attribute >> 7; bool script = superscript || subscript; if (!proportional) { start = 0; // Fixed width font end = fontInfo.charWidth - 1; } if (subscript) { iYPos /= 2.0; iYPos += pixelSizeY * 4.5; } if (script) { iYPos -= pixelSizeY * 4.5; } double hPos = hpos; double headRelative = (doubleWidth ? 2 : 1) * fontInfo.pixelDelta / fontDensity; double destY = vpos * pixelSizeY + iYPos; double destHeight = pixelSizeY * 9.0; double dblStrikeOffset = doubleStrike ? (pixelSizeY / 2.5) : 0.0; printAreaTop = min(printAreaTop, destY); printAreaBottom = max(printAreaBottom, destY + destHeight + dblStrikeOffset); for (unsigned i = start; i < end; ++i) { unsigned charBits = unsigned(charBitmap[i + 1]) << topBits; if (underline) { charBits |= 2; } if (script) { charBits = compress9(charBits); } for (int d = 0; d <= (doubleWidth?1:0); d++) { for (int b = 0; b <= (bold?1:0); ++b) { for (int y = 0; y <= (doubleStrike?1:0); ++y) { double destX = (hPos + (d + b / 2.0) / fontDensity) * pixelSizeX; plot9Dots(destX, destY + y * dblStrikeOffset, charBits); } } } hPos += headRelative; } seekPrinterHeadRelative((1 + end - start) * headRelative); } // class ImagePrinterMSX // MSX-Font taken from NMS8250 BIOS ROM static const byte MSXFontRaw[256 * 8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C, // 1 0x3C, 0x7E, 0xDB, 0xFF, 0xFF, 0xDB, 0x66, 0x3C, // 2 0x6C, 0xFE, 0xFE, 0xFE, 0x7C, 0x38, 0x10, 0x00, // 3 0x10, 0x38, 0x7C, 0xFE, 0x7C, 0x38, 0x10, 0x00, // 4 0x10, 0x38, 0x54, 0xFE, 0x54, 0x10, 0x38, 0x00, // 5 0x10, 0x38, 0x7C, 0xFE, 0xFE, 0x10, 0x38, 0x00, // 6 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, // 7 0xFF, 0xFF, 0xFF, 0xE7, 0xE7, 0xFF, 0xFF, 0xFF, // 8 0x38, 0x44, 0x82, 0x82, 0x82, 0x44, 0x38, 0x00, // 9 0xC7, 0xBB, 0x7D, 0x7D, 0x7D, 0xBB, 0xC7, 0xFF, // 10 0x0F, 0x03, 0x05, 0x79, 0x88, 0x88, 0x88, 0x70, // 11 0x38, 0x44, 0x44, 0x44, 0x38, 0x10, 0x7C, 0x10, // 12 0x30, 0x28, 0x24, 0x24, 0x28, 0x20, 0xE0, 0xC0, // 13 0x3C, 0x24, 0x3C, 0x24, 0x24, 0xE4, 0xDC, 0x18, // 14 0x10, 0x54, 0x38, 0xEE, 0x38, 0x54, 0x10, 0x00, // 15 0x10, 0x10, 0x10, 0x7C, 0x10, 0x10, 0x10, 0x10, // 16 0x10, 0x10, 0x10, 0xFF, 0x00, 0x00, 0x00, 0x00, // 17 0x00, 0x00, 0x00, 0xFF, 0x10, 0x10, 0x10, 0x10, // 18 0x10, 0x10, 0x10, 0xF0, 0x10, 0x10, 0x10, 0x10, // 19 0x10, 0x10, 0x10, 0x1F, 0x10, 0x10, 0x10, 0x10, // 20 0x10, 0x10, 0x10, 0xFF, 0x10, 0x10, 0x10, 0x10, // 21 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, // 22 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, // 23 0x00, 0x00, 0x00, 0x1F, 0x10, 0x10, 0x10, 0x10, // 24 0x00, 0x00, 0x00, 0xF0, 0x10, 0x10, 0x10, 0x10, // 25 0x10, 0x10, 0x10, 0x1F, 0x00, 0x00, 0x00, 0x00, // 26 0x10, 0x10, 0x10, 0xF0, 0x00, 0x00, 0x00, 0x00, // 27 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81, // 28 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, // 29 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, // 30 0x00, 0x10, 0x10, 0xFF, 0x10, 0x10, 0x00, 0x00, // 31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 32 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x20, 0x00, // 33 0x50, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, // 34 0x50, 0x50, 0xF8, 0x50, 0xF8, 0x50, 0x50, 0x00, // 35 0x20, 0x78, 0xA0, 0x70, 0x28, 0xF0, 0x20, 0x00, // 36 0xC0, 0xC8, 0x10, 0x20, 0x40, 0x98, 0x18, 0x00, // 37 0x40, 0xA0, 0x40, 0xA8, 0x90, 0x98, 0x60, 0x00, // 38 0x10, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // 39 0x10, 0x20, 0x40, 0x40, 0x40, 0x20, 0x10, 0x00, // 40 0x40, 0x20, 0x10, 0x10, 0x10, 0x20, 0x40, 0x00, // 41 0x20, 0xA8, 0x70, 0x20, 0x70, 0xA8, 0x20, 0x00, // 42 0x00, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00, 0x00, // 43 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x40, // 44 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, // 45 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x60, 0x00, // 46 0x00, 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, // 47 0x70, 0x88, 0x98, 0xA8, 0xC8, 0x88, 0x70, 0x00, // 48 0x20, 0x60, 0xA0, 0x20, 0x20, 0x20, 0xF8, 0x00, // 49 0x70, 0x88, 0x08, 0x10, 0x60, 0x80, 0xF8, 0x00, // 50 0x70, 0x88, 0x08, 0x30, 0x08, 0x88, 0x70, 0x00, // 51 0x10, 0x30, 0x50, 0x90, 0xF8, 0x10, 0x10, 0x00, // 52 0xF8, 0x80, 0xE0, 0x10, 0x08, 0x10, 0xE0, 0x00, // 53 0x30, 0x40, 0x80, 0xF0, 0x88, 0x88, 0x70, 0x00, // 54 0xF8, 0x88, 0x10, 0x20, 0x20, 0x20, 0x20, 0x00, // 55 0x70, 0x88, 0x88, 0x70, 0x88, 0x88, 0x70, 0x00, // 56 0x70, 0x88, 0x88, 0x78, 0x08, 0x10, 0x60, 0x00, // 57 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, // 58 0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x20, 0x40, // 59 0x18, 0x30, 0x60, 0xC0, 0x60, 0x30, 0x18, 0x00, // 60 0x00, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00, 0x00, // 61 0xC0, 0x60, 0x30, 0x18, 0x30, 0x60, 0xC0, 0x00, // 62 0x70, 0x88, 0x08, 0x10, 0x20, 0x00, 0x20, 0x00, // 63 0x70, 0x88, 0x08, 0x68, 0xA8, 0xA8, 0x70, 0x00, // 64 0x20, 0x50, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x00, // 65 0xF0, 0x48, 0x48, 0x70, 0x48, 0x48, 0xF0, 0x00, // 66 0x30, 0x48, 0x80, 0x80, 0x80, 0x48, 0x30, 0x00, // 67 0xE0, 0x50, 0x48, 0x48, 0x48, 0x50, 0xE0, 0x00, // 68 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0xF8, 0x00, // 69 0xF8, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x00, // 70 0x70, 0x88, 0x80, 0xB8, 0x88, 0x88, 0x70, 0x00, // 71 0x88, 0x88, 0x88, 0xF8, 0x88, 0x88, 0x88, 0x00, // 72 0x70, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, // 73 0x38, 0x10, 0x10, 0x10, 0x90, 0x90, 0x60, 0x00, // 74 0x88, 0x90, 0xA0, 0xC0, 0xA0, 0x90, 0x88, 0x00, // 75 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF8, 0x00, // 76 0x88, 0xD8, 0xA8, 0xA8, 0x88, 0x88, 0x88, 0x00, // 77 0x88, 0xC8, 0xC8, 0xA8, 0x98, 0x98, 0x88, 0x00, // 78 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, // 79 0xF0, 0x88, 0x88, 0xF0, 0x80, 0x80, 0x80, 0x00, // 80 0x70, 0x88, 0x88, 0x88, 0xA8, 0x90, 0x68, 0x00, // 81 0xF0, 0x88, 0x88, 0xF0, 0xA0, 0x90, 0x88, 0x00, // 82 0x70, 0x88, 0x80, 0x70, 0x08, 0x88, 0x70, 0x00, // 83 0xF8, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, // 84 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, // 85 0x88, 0x88, 0x88, 0x88, 0x50, 0x50, 0x20, 0x00, // 86 0x88, 0x88, 0x88, 0xA8, 0xA8, 0xD8, 0x88, 0x00, // 87 0x88, 0x88, 0x50, 0x20, 0x50, 0x88, 0x88, 0x00, // 88 0x88, 0x88, 0x88, 0x70, 0x20, 0x20, 0x20, 0x00, // 89 0xF8, 0x08, 0x10, 0x20, 0x40, 0x80, 0xF8, 0x00, // 90 0x70, 0x40, 0x40, 0x40, 0x40, 0x40, 0x70, 0x00, // 91 0x00, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x00, // 92 0x70, 0x10, 0x10, 0x10, 0x10, 0x10, 0x70, 0x00, // 93 0x20, 0x50, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, // 94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, // 95 0x40, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 0x00, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, // 97 0x80, 0x80, 0xB0, 0xC8, 0x88, 0xC8, 0xB0, 0x00, // 98 0x00, 0x00, 0x70, 0x88, 0x80, 0x88, 0x70, 0x00, // 99 0x08, 0x08, 0x68, 0x98, 0x88, 0x98, 0x68, 0x00, // 100 0x00, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, // 101 0x10, 0x28, 0x20, 0xF8, 0x20, 0x20, 0x20, 0x00, // 102 0x00, 0x00, 0x68, 0x98, 0x98, 0x68, 0x08, 0x70, // 103 0x80, 0x80, 0xF0, 0x88, 0x88, 0x88, 0x88, 0x00, // 104 0x20, 0x00, 0x60, 0x20, 0x20, 0x20, 0x70, 0x00, // 105 0x10, 0x00, 0x30, 0x10, 0x10, 0x10, 0x90, 0x60, // 106 0x40, 0x40, 0x48, 0x50, 0x60, 0x50, 0x48, 0x00, // 107 0x60, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x00, // 108 0x00, 0x00, 0xD0, 0xA8, 0xA8, 0xA8, 0xA8, 0x00, // 109 0x00, 0x00, 0xB0, 0xC8, 0x88, 0x88, 0x88, 0x00, // 110 0x00, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, // 111 0x00, 0x00, 0xB0, 0xC8, 0xC8, 0xB0, 0x80, 0x80, // 112 0x00, 0x00, 0x68, 0x98, 0x98, 0x68, 0x08, 0x08, // 113 0x00, 0x00, 0xB0, 0xC8, 0x80, 0x80, 0x80, 0x00, // 114 0x00, 0x00, 0x78, 0x80, 0xF0, 0x08, 0xF0, 0x00, // 115 0x40, 0x40, 0xF0, 0x40, 0x40, 0x48, 0x30, 0x00, // 116 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x68, 0x00, // 117 0x00, 0x00, 0x88, 0x88, 0x88, 0x50, 0x20, 0x00, // 118 0x00, 0x00, 0x88, 0xA8, 0xA8, 0xA8, 0x50, 0x00, // 119 0x00, 0x00, 0x88, 0x50, 0x20, 0x50, 0x88, 0x00, // 120 0x00, 0x00, 0x88, 0x88, 0x98, 0x68, 0x08, 0x70, // 121 0x00, 0x00, 0xF8, 0x10, 0x20, 0x40, 0xF8, 0x00, // 122 0x18, 0x20, 0x20, 0x40, 0x20, 0x20, 0x18, 0x00, // 123 0x20, 0x20, 0x20, 0x00, 0x20, 0x20, 0x20, 0x00, // 124 0xC0, 0x20, 0x20, 0x10, 0x20, 0x20, 0xC0, 0x00, // 125 0x40, 0xA8, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, // 126 0x00, 0x00, 0x20, 0x50, 0xF8, 0x00, 0x00, 0x00, // 127 0x70, 0x88, 0x80, 0x80, 0x88, 0x70, 0x20, 0x60, // 128 0x90, 0x00, 0x00, 0x90, 0x90, 0x90, 0x68, 0x00, // 129 0x10, 0x20, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, // 130 0x20, 0x50, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, // 131 0x48, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, // 132 0x20, 0x10, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, // 133 0x20, 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, // 134 0x00, 0x70, 0x80, 0x80, 0x80, 0x70, 0x10, 0x60, // 135 0x20, 0x50, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, // 136 0x50, 0x00, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, // 137 0x20, 0x10, 0x70, 0x88, 0xF8, 0x80, 0x70, 0x00, // 138 0x50, 0x00, 0x00, 0x60, 0x20, 0x20, 0x70, 0x00, // 139 0x20, 0x50, 0x00, 0x60, 0x20, 0x20, 0x70, 0x00, // 140 0x40, 0x20, 0x00, 0x60, 0x20, 0x20, 0x70, 0x00, // 141 0x50, 0x00, 0x20, 0x50, 0x88, 0xF8, 0x88, 0x00, // 142 0x20, 0x00, 0x20, 0x50, 0x88, 0xF8, 0x88, 0x00, // 143 0x10, 0x20, 0xF8, 0x80, 0xF0, 0x80, 0xF8, 0x00, // 144 0x00, 0x00, 0x6C, 0x12, 0x7E, 0x90, 0x6E, 0x00, // 145 0x3E, 0x50, 0x90, 0x9C, 0xF0, 0x90, 0x9E, 0x00, // 146 0x60, 0x90, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, // 147 0x90, 0x00, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, // 148 0x40, 0x20, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, // 149 0x40, 0xA0, 0x00, 0xA0, 0xA0, 0xA0, 0x50, 0x00, // 150 0x40, 0x20, 0x00, 0xA0, 0xA0, 0xA0, 0x50, 0x00, // 151 0x90, 0x00, 0x90, 0x90, 0xB0, 0x50, 0x10, 0xE0, // 152 0x50, 0x00, 0x70, 0x88, 0x88, 0x88, 0x70, 0x00, // 153 0x50, 0x00, 0x88, 0x88, 0x88, 0x88, 0x70, 0x00, // 154 0x20, 0x20, 0x78, 0x80, 0x80, 0x78, 0x20, 0x20, // 155 0x18, 0x24, 0x20, 0xF8, 0x20, 0xE2, 0x5C, 0x00, // 156 0x88, 0x50, 0x20, 0xF8, 0x20, 0xF8, 0x20, 0x00, // 157 0xC0, 0xA0, 0xA0, 0xC8, 0x9C, 0x88, 0x88, 0x8C, // 158 0x18, 0x20, 0x20, 0xF8, 0x20, 0x20, 0x20, 0x40, // 159 0x10, 0x20, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, // 160 0x10, 0x20, 0x00, 0x60, 0x20, 0x20, 0x70, 0x00, // 161 0x20, 0x40, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, // 162 0x20, 0x40, 0x00, 0x90, 0x90, 0x90, 0x68, 0x00, // 163 0x50, 0xA0, 0x00, 0xA0, 0xD0, 0x90, 0x90, 0x00, // 164 0x28, 0x50, 0x00, 0xC8, 0xA8, 0x98, 0x88, 0x00, // 165 0x00, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, 0xF8, // 166 0x00, 0x60, 0x90, 0x90, 0x90, 0x60, 0x00, 0xF0, // 167 0x20, 0x00, 0x20, 0x40, 0x80, 0x88, 0x70, 0x00, // 168 0x00, 0x00, 0x00, 0xF8, 0x80, 0x80, 0x00, 0x00, // 169 0x00, 0x00, 0x00, 0xF8, 0x08, 0x08, 0x00, 0x00, // 170 0x84, 0x88, 0x90, 0xA8, 0x54, 0x84, 0x08, 0x1C, // 171 0x84, 0x88, 0x90, 0xA8, 0x58, 0xA8, 0x3C, 0x08, // 172 0x20, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x00, // 173 0x00, 0x00, 0x24, 0x48, 0x90, 0x48, 0x24, 0x00, // 174 0x00, 0x00, 0x90, 0x48, 0x24, 0x48, 0x90, 0x00, // 175 0x28, 0x50, 0x20, 0x50, 0x88, 0xF8, 0x88, 0x00, // 176 0x28, 0x50, 0x70, 0x08, 0x78, 0x88, 0x78, 0x00, // 177 0x28, 0x50, 0x00, 0x70, 0x20, 0x20, 0x70, 0x00, // 178 0x28, 0x50, 0x00, 0x20, 0x20, 0x20, 0x70, 0x00, // 179 0x28, 0x50, 0x00, 0x70, 0x88, 0x88, 0x70, 0x00, // 180 0x50, 0xA0, 0x00, 0x60, 0x90, 0x90, 0x60, 0x00, // 181 0x28, 0x50, 0x00, 0x88, 0x88, 0x88, 0x70, 0x00, // 182 0x50, 0xA0, 0x00, 0xA0, 0xA0, 0xA0, 0x50, 0x00, // 183 0xFC, 0x48, 0x48, 0x48, 0xE8, 0x08, 0x50, 0x20, // 184 0x00, 0x50, 0x00, 0x50, 0x50, 0x50, 0x10, 0x20, // 185 0xC0, 0x44, 0xC8, 0x54, 0xEC, 0x54, 0x9E, 0x04, // 186 0x10, 0xA8, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, // 187 0x00, 0x20, 0x50, 0x88, 0x50, 0x20, 0x00, 0x00, // 188 0x88, 0x10, 0x20, 0x40, 0x80, 0x28, 0x00, 0x00, // 189 0x7C, 0xA8, 0xA8, 0x68, 0x28, 0x28, 0x28, 0x00, // 190 0x38, 0x40, 0x30, 0x48, 0x48, 0x30, 0x08, 0x70, // 191 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, // 192 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, // 193 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 194 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 195 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x00, 0x00, 0x00, // 196 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, // 197 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, // 198 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, // 199 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, // 200 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, // 201 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, // 202 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, // 203 0x88, 0x44, 0x22, 0x11, 0x88, 0x44, 0x22, 0x11, // 204 0xFE, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, // 205 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0xFE, // 206 0x80, 0xC0, 0xE0, 0xF0, 0xE0, 0xC0, 0x80, 0x00, // 207 0x01, 0x03, 0x07, 0x0F, 0x07, 0x03, 0x01, 0x00, // 208 0xFF, 0x7E, 0x3C, 0x18, 0x18, 0x3C, 0x7E, 0xFF, // 209 0x81, 0xC3, 0xE7, 0xFF, 0xFF, 0xE7, 0xC3, 0x81, // 210 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00, 0x00, 0x00, // 211 0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x0F, 0x0F, // 212 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, // 213 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, // 214 0x33, 0x33, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, // 215 0x00, 0x20, 0x20, 0x50, 0x50, 0x88, 0xF8, 0x00, // 216 0x20, 0x20, 0x70, 0x20, 0x70, 0x20, 0x20, 0x00, // 217 0x00, 0x00, 0x00, 0x50, 0x88, 0xA8, 0x50, 0x00, // 218 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 219 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, // 220 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, // 221 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, // 222 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, // 223 0x00, 0x00, 0x68, 0x90, 0x90, 0x90, 0x68, 0x00, // 224 0x30, 0x48, 0x48, 0x70, 0x48, 0x48, 0x70, 0xC0, // 225 0xF8, 0x88, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, // 226 0xF8, 0x50, 0x50, 0x50, 0x50, 0x50, 0x98, 0x00, // 227 0xF8, 0x88, 0x40, 0x20, 0x40, 0x88, 0xF8, 0x00, // 228 0x00, 0x00, 0x78, 0x90, 0x90, 0x90, 0x60, 0x00, // 229 0x00, 0x50, 0x50, 0x50, 0x50, 0x68, 0x80, 0x80, // 230 0x00, 0x50, 0xA0, 0x20, 0x20, 0x20, 0x20, 0x00, // 231 0xF8, 0x20, 0x70, 0xA8, 0xA8, 0x70, 0x20, 0xF8, // 232 0x20, 0x50, 0x88, 0xF8, 0x88, 0x50, 0x20, 0x00, // 233 0x70, 0x88, 0x88, 0x88, 0x50, 0x50, 0xD8, 0x00, // 234 0x30, 0x40, 0x40, 0x20, 0x50, 0x50, 0x50, 0x20, // 235 0x00, 0x00, 0x00, 0x50, 0xA8, 0xA8, 0x50, 0x00, // 236 0x08, 0x70, 0xA8, 0xA8, 0xA8, 0x70, 0x80, 0x00, // 237 0x38, 0x40, 0x80, 0xF8, 0x80, 0x40, 0x38, 0x00, // 238 0x70, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x00, // 239 0x00, 0xF8, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x00, // 240 0x20, 0x20, 0xF8, 0x20, 0x20, 0x00, 0xF8, 0x00, // 241 0xC0, 0x30, 0x08, 0x30, 0xC0, 0x00, 0xF8, 0x00, // 242 0x18, 0x60, 0x80, 0x60, 0x18, 0x00, 0xF8, 0x00, // 243 0x10, 0x28, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // 244 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xA0, 0x40, // 245 0x00, 0x20, 0x00, 0xF8, 0x00, 0x20, 0x00, 0x00, // 246 0x00, 0x50, 0xA0, 0x00, 0x50, 0xA0, 0x00, 0x00, // 247 0x00, 0x18, 0x24, 0x24, 0x18, 0x00, 0x00, 0x00, // 248 0x00, 0x30, 0x78, 0x78, 0x30, 0x00, 0x00, 0x00, // 249 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, // 250 0x3E, 0x20, 0x20, 0x20, 0xA0, 0x60, 0x20, 0x00, // 251 0xA0, 0x50, 0x50, 0x50, 0x00, 0x00, 0x00, 0x00, // 252 0x40, 0xA0, 0x20, 0x40, 0xE0, 0x00, 0x00, 0x00, // 253 0x00, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x00, // 254 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 255 }; static byte MSXFont[256 * 9]; ImagePrinterMSX::ImagePrinterMSX(MSXMotherBoard& motherBoard) : ImagePrinter(motherBoard, true) { msxPrnSetFont(MSXFontRaw); resetEmulatedPrinter(); } const string& ImagePrinterMSX::getName() const { static const string name("msx-printer"); return name; } string_ref ImagePrinterMSX::getDescription() const { // TODO which printer type return "Emulate MSX printer, prints to image."; } void ImagePrinterMSX::msxPrnSetFont(const byte* msxBits) { // Convert MSX printer font to Epson printer font byte* font = MSXFont; for (int i = 0; i < 256; ++i) { byte oneBits = 0; int start = -1; int end = 0; // Rotate MSX character for (int j = 0; j < 8; ++j) { byte charBits = 0; for (int k = 0; k < 8; ++k) { charBits |= ((msxBits[7 - k] >> (7 - j)) & 1) << k; } oneBits |= charBits; if (oneBits != 0 && start < 0) start = j; if (charBits != 0) end = j; font[j + 1] = charBits; } end = end + 1; if (start < 0 || start >= 7) start = 0; if (end == 1) end = 5; if (end >= 7) end = 7; font[0] = (start << 4) | end; font += 9; msxBits += 8; } } void ImagePrinterMSX::getNumberOfDots(unsigned& dotsX, unsigned& dotsY) { dotsX = 825; dotsY = 825; } void ImagePrinterMSX::resetSettings() { lineFeed = 12.0; leftBorder = 48; rightBorder = 800; graphDensity = 1.0; fontDensity = 1.0; pageTop = 48; lines = 72; fontWidth = 8; eightBit = -1; // note: this only overwrites 9/12 of the fontInfo.rom array. memcpy(fontInfo.rom, MSXFont, sizeof(MSXFont)); fontInfo.charWidth = 9; fontInfo.pixelDelta = 1.0; fontInfo.useRam = false; } unsigned ImagePrinterMSX::calcEscSequenceLength(byte character) { switch (character) { case 'C': return 1; case 'T': case 'Z': return 2; case 'O': case '\\': case 'L': case '/': return 3; case 'S': return 4; case 'G': return 7; default: return 0; } } unsigned ImagePrinterMSX::parseNumber(unsigned sizeStart, unsigned sizeChars) { unsigned Value = 0; while (sizeChars--) { Value = Value * 10; byte data = abEscSeq[sizeStart++]; if (data >= '0' && data <= '9') { Value += unsigned(data - '0'); } } return Value; } void ImagePrinterMSX::processEscSequence() { switch (abEscSeq[0]) { case 'N': proportional = false; fontDensity = 1.0; break; case 'E': proportional = false; fontDensity = 1.40; break; case 'Q': proportional = false; fontDensity = 1.72; break; case 'P': proportional = true; fontDensity = 0.90; break; case '!': letterQuality = true; break; case '\"': letterQuality = false; break; case 'C': switch (abEscSeq[1]) { case 'D': doubleStrike = true; break; case 'd': doubleStrike = false; break; case 'I': italic = true; break; case 'i': italic = false; break; case 'B': bold = true; break; case 'b': bold = false; break; case 'S': superscript = true; break; case 's': superscript = false; break; case 'U': subscript = true; break; case 'u': subscript = false; break; } break; case '(': // ???: Set a horizontal tab position break; case ')': // ???: Partially delete a horizontal tab position break; case '2': // ???: Clear horizontal tabs break; case 'O': switch (abEscSeq[1]) { case 'S': perforationSkip = parseNumber(2, 2); break; case 'I': // ???: Set page-height(inches) break; default: // ???: Set page-height (lines) break; } break; case '/': // Right margin break; case 'L': leftBorder = parseNumber(1, 3); break; case 'A': // ???: Line-feed 1/6" lineFeed = 12.0; break; case 'B': // ???: Line-feed 1/9" lineFeed = 8.0; break; case 'T': // ???: Line-feed nn/144" lineFeed = parseNumber(1, 2) / 2.0; break; case 'Z': // ???: Line-feed nn/216" lineFeed = parseNumber(1, 2) / 3.0; break; case '[': // ???: Uni-directional printing break; case ']': // ???: Bi-directional printing break; case 'p': detectPaperOut = true; break; case 'q': detectPaperOut = false; break; case 13: // (ESC, CR) Move printer-head to end-position break; case '@': resetEmulatedPrinter(); break; case '\\': rightBorder = parseNumber(1, 3); break; case 'G': graphDensity = parseNumber(1, 3) / 100.0; if (graphDensity < 0.1) { graphDensity = 0.1; } sizeRemainingDataBytes = parseNumber(4, 4); break; case 'S': // Print graphics, density depending on font sizeRemainingDataBytes = parseNumber(1, 4); break; case 'X': underline = true; break; case 'Y': underline = false; break; case '&': case '$': japanese = !japanese; break; case 'f': // ???: Scroll paper forward break; case 'r': // ???: Scroll paper back break; } } void ImagePrinterMSX::processCharacter(byte data) { if (alternateChar) { // Print SOH-preceded character printVisibleCharacter(data & 0x1F); alternateChar = false; } else { switch (data) { case 1: // SOH: A symbolcode preceding code alternateChar = true; break; case 7: // BEL: Audible beep (buzzer, 0.3s) break; case 8: // BS: Backstep (1 Character) // TODO: fix for other font-sizes hpos -= 8; if (hpos < leftBorder) { hpos = leftBorder; } break; case 9: { // HAT: Horizontal tabulator // TODO: fix for other font-sizes hpos = ((unsigned(hpos) + 64 - leftBorder) & ~63) + leftBorder; if (hpos < rightBorder) { break; } hpos = leftBorder; // fall thru: CR/LF } case 10: // LF: Carriage return + Line feed case 11: // VT: Vertical tabulator (like LF) //hpos = leftBorder; vpos += lineFeed; if (vpos >= pageHeight) { flushEmulatedPrinter(); } break; case 12: // FF: Form feed ensurePrintPage(); flushEmulatedPrinter(); break; case 13: // CR: Carriage return hpos = leftBorder; break; case 14: // SO: Double character-width on doubleWidth = true; break; case 15: // SI: Double character-width off doubleWidth = false; break; case 27: escSequence = true; break; default: if (data >= 32) { // Yes, we can print it! printVisibleCharacter(data); } break; } } } template void ImagePrinterMSX::serialize(Archive& /*ar*/, unsigned /*version*/) { // TODO is this worth it? } INSTANTIATE_SERIALIZE_METHODS(ImagePrinterMSX); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, ImagePrinterMSX, "ImagePrinterMSX"); // class ImagePrinterEpson static const byte EpsonFontRom[] = { 0x8b, 0x04, 0x0a, 0x20, 0x8a, 0x60, 0x0a, 0x20, 0x1c, 0x02, 0x00, 0x00, // 0 0x8b, 0x1c, 0x22, 0x08, 0xa2, 0x48, 0x22, 0x08, 0x22, 0x18, 0x00, 0x00, // 1 0x9b, 0x00, 0x3c, 0x00, 0x82, 0x40, 0x02, 0x00, 0x3c, 0x02, 0x00, 0x00, // 2 0x9a, 0x00, 0x1c, 0x22, 0x80, 0x62, 0x00, 0x22, 0x1c, 0x00, 0x00, 0x00, // 3 0x96, 0x00, 0x12, 0x80, 0x5e, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 4 0xa7, 0x00, 0x00, 0x40, 0xa0, 0x00, 0xa0, 0x40, 0x00, 0x00, 0x00, 0x00, // 5 0x8b, 0x12, 0x00, 0x7e, 0x80, 0x12, 0x80, 0x02, 0x80, 0x42, 0x00, 0x00, // 6 0xc8, 0x00, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7 0x8b, 0x06, 0x00, 0x09, 0x00, 0x51, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, // 8 0x8b, 0x5e, 0x80, 0x10, 0x80, 0x08, 0x40, 0x04, 0x40, 0x9e, 0x00, 0x00, // 9 0x8a, 0x40, 0x9e, 0x00, 0x90, 0x40, 0x10, 0x4e, 0x80, 0x00, 0x00, 0x00, // 10 0x8b, 0x92, 0x28, 0x44, 0x00, 0x44, 0x00, 0x44, 0x28, 0x92, 0x00, 0x00, // 11 0x8b, 0xfe, 0x00, 0xa0, 0x00, 0x48, 0x00, 0x1e, 0x00, 0x0a, 0x00, 0x00, // 12 0x8b, 0x06, 0x08, 0x54, 0xa0, 0x04, 0xa0, 0x54, 0x08, 0x06, 0x00, 0x00, // 13 0x8b, 0x04, 0x0a, 0x20, 0x0a, 0xa0, 0x0a, 0x20, 0x1c, 0x02, 0x00, 0x00, // 14 0x0a, 0x38, 0x44, 0x01, 0x44, 0x01, 0x46, 0x00, 0x44, 0x00, 0x00, 0x00, // 15 0x9a, 0x00, 0x50, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x14, 0x00, 0x00, 0x00, // 16 0x8a, 0x7e, 0x80, 0x00, 0x80, 0x12, 0x80, 0x12, 0x6c, 0x00, 0x00, 0x00, // 17 0x8b, 0x3e, 0x40, 0x90, 0x00, 0xfe, 0x00, 0x92, 0x00, 0x92, 0x00, 0x00, // 18 0x8b, 0x2c, 0x02, 0x28, 0x02, 0x1c, 0x20, 0x0a, 0x20, 0x1a, 0x00, 0x00, // 19 0x8b, 0x3a, 0x44, 0x00, 0x8a, 0x10, 0xa2, 0x00, 0x44, 0xb8, 0x00, 0x00, // 20 0x8b, 0x02, 0x08, 0x14, 0x22, 0x08, 0x22, 0x14, 0x08, 0x20, 0x00, 0x00, // 21 0xa9, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // 22 0x8b, 0x06, 0x88, 0x14, 0x20, 0x44, 0x20, 0x14, 0x88, 0x06, 0x00, 0x00, // 23 0x8b, 0x1c, 0xa2, 0x00, 0x22, 0x00, 0x22, 0x00, 0xa2, 0x1c, 0x00, 0x00, // 24 0x8b, 0x3c, 0x82, 0x00, 0x02, 0x00, 0x02, 0x00, 0x82, 0x3c, 0x00, 0x00, // 25 0x8b, 0x04, 0x0a, 0xa0, 0x0a, 0x20, 0x0a, 0xa0, 0x1c, 0x02, 0x00, 0x00, // 26 0x9a, 0x00, 0x1c, 0xa2, 0x00, 0x22, 0x00, 0xa2, 0x1c, 0x00, 0x00, 0x00, // 27 0x8a, 0x3c, 0x80, 0x02, 0x00, 0x02, 0x80, 0x3c, 0x02, 0x00, 0x00, 0x00, // 28 0x8b, 0x3e, 0x00, 0x2a, 0x00, 0x6a, 0x80, 0x2a, 0x00, 0x22, 0x00, 0x00, // 29 0x8b, 0x1c, 0x22, 0x08, 0x22, 0x48, 0xa2, 0x08, 0x22, 0x18, 0x00, 0x00, // 30 0x8b, 0xa8, 0x00, 0x68, 0x00, 0x3e, 0x00, 0x68, 0x00, 0xa8, 0x00, 0x00, // 31 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 32 0xc8, 0x00, 0x00, 0x00, 0x00, 0xf2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 33 0xa9, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, // 34 0x8b, 0x28, 0x00, 0xfe, 0x00, 0x28, 0x00, 0xfe, 0x00, 0x28, 0x00, 0x00, // 35 0x8b, 0x24, 0x00, 0x54, 0x00, 0xfe, 0x00, 0x54, 0x00, 0x48, 0x00, 0x00, // 36 0x8b, 0xc0, 0x02, 0xc4, 0x08, 0x10, 0x20, 0x46, 0x80, 0x06, 0x00, 0x00, // 37 0x8b, 0x4c, 0xa0, 0x12, 0xa0, 0x4a, 0x00, 0x04, 0x08, 0x12, 0x00, 0x00, // 38 0xc8, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, // 39 0xc9, 0x00, 0x00, 0x00, 0x00, 0x38, 0x44, 0x82, 0x00, 0x00, 0x00, 0x00, // 40 0xa7, 0x00, 0x00, 0x82, 0x44, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 41 0x8b, 0x10, 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, 0x00, 0x10, 0x00, 0x00, // 42 0x8b, 0x10, 0x00, 0x10, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, // 43 0x39, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, // 44 0x8b, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, // 45 0xa8, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 46 0x9a, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, // 47 0x8b, 0x38, 0x44, 0x00, 0x82, 0x00, 0x82, 0x00, 0x44, 0x38, 0x00, 0x00, // 48 0xa9, 0x00, 0x00, 0x42, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, // 49 0x8b, 0x42, 0x80, 0x06, 0x80, 0x0a, 0x80, 0x12, 0x80, 0x62, 0x00, 0x00, // 50 0x8b, 0x84, 0x00, 0x82, 0x00, 0xa2, 0x00, 0xd2, 0x00, 0x8c, 0x00, 0x00, // 51 0x8b, 0x08, 0x10, 0x28, 0x40, 0x88, 0x00, 0xfe, 0x00, 0x08, 0x00, 0x00, // 52 0x8b, 0xe4, 0x02, 0xa0, 0x02, 0xa0, 0x02, 0xa0, 0x02, 0x9c, 0x00, 0x00, // 53 0x8b, 0x0c, 0x12, 0x20, 0x52, 0x80, 0x12, 0x00, 0x12, 0x0c, 0x00, 0x00, // 54 0x8b, 0x80, 0x00, 0x82, 0x04, 0x88, 0x10, 0xa0, 0x40, 0x80, 0x00, 0x00, // 55 0x8b, 0x6c, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x6c, 0x00, 0x00, // 56 0x8b, 0x60, 0x90, 0x00, 0x90, 0x02, 0x94, 0x08, 0x90, 0x60, 0x00, 0x00, // 57 0xa7, 0x00, 0x00, 0x36, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 58 0x27, 0x00, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 59 0x89, 0x10, 0x00, 0x28, 0x00, 0x44, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, // 60 0x8b, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x28, 0x00, 0x00, // 61 0xab, 0x00, 0x00, 0x82, 0x00, 0x44, 0x00, 0x28, 0x00, 0x10, 0x00, 0x00, // 62 0x8b, 0x40, 0x80, 0x00, 0x80, 0x0a, 0x80, 0x10, 0x80, 0x60, 0x00, 0x00, // 63 0x8b, 0x38, 0x44, 0x82, 0x10, 0xaa, 0x00, 0xaa, 0x00, 0x7a, 0x00, 0x00, // 64 0x8b, 0x1e, 0x20, 0x48, 0x80, 0x08, 0x80, 0x48, 0x20, 0x1e, 0x00, 0x00, // 65 0x8b, 0x82, 0x7c, 0x82, 0x10, 0x82, 0x10, 0x82, 0x10, 0x6c, 0x00, 0x00, // 66 0x8b, 0x7c, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x44, 0x00, 0x00, // 67 0x8b, 0x82, 0x7c, 0x82, 0x00, 0x82, 0x00, 0x82, 0x44, 0x38, 0x00, 0x00, // 68 0x8b, 0xfe, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x82, 0x00, 0x00, // 69 0x8b, 0xfe, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x80, 0x00, 0x00, // 70 0x8b, 0x7c, 0x82, 0x00, 0x82, 0x10, 0x82, 0x10, 0x82, 0x5c, 0x00, 0x00, // 71 0x8b, 0xfe, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0xfe, 0x00, 0x00, // 72 0xa9, 0x00, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, // 73 0x8a, 0x0c, 0x02, 0x00, 0x82, 0x00, 0x82, 0x7c, 0x80, 0x00, 0x00, 0x00, // 74 0x8b, 0xfe, 0x00, 0x10, 0x00, 0x28, 0x00, 0x44, 0x00, 0x82, 0x00, 0x00, // 75 0x8b, 0xfe, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, // 76 0x8b, 0xfe, 0x00, 0x40, 0x20, 0x10, 0x20, 0x40, 0x00, 0xfe, 0x00, 0x00, // 77 0x8b, 0xfe, 0x00, 0x40, 0x20, 0x10, 0x08, 0x04, 0x00, 0xfe, 0x00, 0x00, // 78 0x8b, 0x7c, 0x82, 0x00, 0x82, 0x00, 0x82, 0x00, 0x82, 0x7c, 0x00, 0x00, // 79 0x8b, 0xfe, 0x00, 0x90, 0x00, 0x90, 0x00, 0x90, 0x00, 0x60, 0x00, 0x00, // 80 0x8b, 0x7c, 0x82, 0x00, 0x82, 0x08, 0x82, 0x04, 0x80, 0x7a, 0x00, 0x00, // 81 0x8b, 0xfe, 0x00, 0x90, 0x00, 0x90, 0x00, 0x98, 0x04, 0x62, 0x00, 0x00, // 82 0x8b, 0x64, 0x92, 0x00, 0x92, 0x00, 0x92, 0x00, 0x92, 0x4c, 0x00, 0x00, // 83 0x8b, 0x80, 0x00, 0x80, 0x00, 0xfe, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, // 84 0x8b, 0xfc, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x02, 0xfc, 0x00, 0x00, // 85 0x8b, 0xe0, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0xe0, 0x00, 0x00, // 86 0x8b, 0xfc, 0x02, 0x04, 0x08, 0x30, 0x08, 0x04, 0x02, 0xfc, 0x00, 0x00, // 87 0x9a, 0x00, 0x82, 0x44, 0x28, 0x10, 0x28, 0x44, 0x82, 0x00, 0x00, 0x00, // 88 0x8b, 0x80, 0x40, 0x20, 0x10, 0x0e, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, // 89 0x9a, 0x00, 0x82, 0x04, 0x8a, 0x10, 0xa2, 0x40, 0x82, 0x00, 0x00, 0x00, // 90 0xa9, 0x00, 0x00, 0xfe, 0x00, 0x82, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, // 91 0x9a, 0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x00, 0x00, 0x00, // 92 0xa9, 0x00, 0x00, 0x82, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, // 93 0x8b, 0x20, 0x00, 0x40, 0x00, 0x80, 0x00, 0x40, 0x00, 0x20, 0x00, 0x00, // 94 0x0b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, // 95 0xb7, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 96 0x8b, 0x04, 0x0a, 0x20, 0x0a, 0x20, 0x0a, 0x20, 0x1c, 0x02, 0x00, 0x00, // 97 0x8a, 0xfe, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x1c, 0x00, 0x00, 0x00, // 98 0x8a, 0x1c, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x00, 0x00, // 99 0x8a, 0x1c, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0xfe, 0x00, 0x00, 0x00, // 100 0x8b, 0x1c, 0x22, 0x08, 0x22, 0x08, 0x22, 0x08, 0x22, 0x18, 0x00, 0x00, // 101 0x89, 0x10, 0x00, 0x10, 0x7e, 0x90, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, // 102 0x0a, 0x38, 0x44, 0x01, 0x44, 0x01, 0x44, 0x01, 0x7e, 0x00, 0x00, 0x00, // 103 0x8a, 0xfe, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x1e, 0x00, 0x00, 0x00, // 104 0x98, 0x00, 0x22, 0x00, 0xbe, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 105 0x99, 0x00, 0x01, 0x00, 0x01, 0x20, 0x01, 0xbe, 0x00, 0x00, 0x00, 0x00, // 106 0x9a, 0x00, 0xfe, 0x00, 0x08, 0x00, 0x14, 0x00, 0x22, 0x00, 0x00, 0x00, // 107 0x98, 0x00, 0x82, 0x00, 0xfe, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 108 0x8b, 0x1e, 0x20, 0x00, 0x20, 0x1e, 0x20, 0x00, 0x20, 0x1e, 0x00, 0x00, // 109 0x8a, 0x3e, 0x00, 0x20, 0x00, 0x20, 0x00, 0x20, 0x1e, 0x00, 0x00, 0x00, // 110 0x8b, 0x1c, 0x22, 0x00, 0x22, 0x00, 0x22, 0x00, 0x22, 0x1c, 0x00, 0x00, // 111 0x0a, 0x7f, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x38, 0x00, 0x00, 0x00, // 112 0x1b, 0x00, 0x38, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x7e, 0x00, 0x00, // 113 0x8a, 0x3e, 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, // 114 0x8b, 0x10, 0x2a, 0x00, 0x2a, 0x00, 0x2a, 0x00, 0x2a, 0x04, 0x00, 0x00, // 115 0x8a, 0x20, 0x00, 0x7c, 0x02, 0x20, 0x02, 0x20, 0x02, 0x00, 0x00, 0x00, // 116 0x8b, 0x3c, 0x02, 0x00, 0x02, 0x00, 0x02, 0x00, 0x3c, 0x02, 0x00, 0x00, // 117 0x8b, 0x20, 0x10, 0x08, 0x04, 0x02, 0x04, 0x08, 0x10, 0x20, 0x00, 0x00, // 118 0x8b, 0x3c, 0x02, 0x04, 0x08, 0x10, 0x08, 0x04, 0x02, 0x3c, 0x00, 0x00, // 119 0x89, 0x22, 0x14, 0x00, 0x08, 0x00, 0x14, 0x22, 0x00, 0x00, 0x00, 0x00, // 120 0x0b, 0x40, 0x20, 0x11, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, // 121 0x89, 0x22, 0x04, 0x22, 0x08, 0x22, 0x10, 0x22, 0x00, 0x00, 0x00, 0x00, // 122 0xaa, 0x00, 0x00, 0x10, 0x00, 0x6c, 0x82, 0x00, 0x82, 0x00, 0x00, 0x00, // 123 0xc7, 0x00, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 124 0xaa, 0x00, 0x82, 0x00, 0x82, 0x6c, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // 125 0x8b, 0x40, 0x80, 0x00, 0x80, 0x40, 0x20, 0x00, 0x20, 0x40, 0x00, 0x00, // 126 0x8b, 0x7c, 0x82, 0x04, 0x8a, 0x10, 0xa2, 0x40, 0x82, 0x7c, 0x00, 0x00, // 127 0x8a, 0x04, 0x0a, 0x80, 0x2a, 0x60, 0x0a, 0x24, 0x1a, 0x00, 0x00, 0x00, // 128 0x8a, 0x0c, 0x12, 0x28, 0x82, 0x68, 0x02, 0x28, 0x10, 0x00, 0x00, 0x00, // 129 0x8a, 0x0c, 0x32, 0x00, 0x82, 0x40, 0x02, 0x0c, 0x32, 0x00, 0x00, 0x00, // 130 0x8a, 0x0c, 0x12, 0x00, 0xa0, 0x42, 0x00, 0x24, 0x18, 0x00, 0x00, 0x00, // 131 0x98, 0x00, 0x02, 0x00, 0x16, 0x88, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, // 132 0xa9, 0x00, 0x00, 0x40, 0xa0, 0x00, 0xa0, 0x40, 0x00, 0x00, 0x00, 0x00, // 133 0x8b, 0x12, 0x00, 0x1e, 0x60, 0x12, 0x80, 0x12, 0x80, 0x40, 0x00, 0x00, // 134 0x9a, 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00, 0x80, 0x00, 0x00, 0x00, // 135 0x8a, 0x06, 0x01, 0x08, 0x01, 0x10, 0x21, 0x80, 0x02, 0x00, 0x00, 0x00, // 136 0x8b, 0x06, 0x58, 0x80, 0x08, 0x84, 0x40, 0x06, 0x58, 0x80, 0x00, 0x00, // 137 0x8b, 0x12, 0x4c, 0x80, 0x10, 0x80, 0x50, 0x02, 0x4c, 0x80, 0x00, 0x00, // 138 0x8b, 0x02, 0x18, 0x24, 0x80, 0x44, 0x02, 0x48, 0x30, 0x80, 0x00, 0x00, // 139 0x8b, 0x06, 0x38, 0xc0, 0x20, 0x88, 0x26, 0xd8, 0x02, 0x08, 0x00, 0x00, // 140 0x8b, 0x02, 0x04, 0x08, 0x14, 0x40, 0xa4, 0x00, 0xbe, 0x40, 0x00, 0x00, // 141 0x8a, 0x04, 0x0a, 0x20, 0x0a, 0x20, 0x8a, 0x24, 0x1a, 0x00, 0x00, 0x00, // 142 0x1b, 0x00, 0x18, 0x21, 0x04, 0x41, 0x06, 0x40, 0x04, 0x40, 0x00, 0x00, // 143 0x8b, 0x02, 0x10, 0x6a, 0x00, 0xaa, 0x00, 0xac, 0x10, 0x80, 0x00, 0x00, // 144 0x8a, 0x06, 0x18, 0x60, 0x00, 0x82, 0x10, 0x82, 0x6c, 0x00, 0x00, 0x00, // 145 0x8b, 0x0e, 0x30, 0x40, 0x90, 0x0e, 0x70, 0x82, 0x10, 0x82, 0x00, 0x00, // 146 0x8b, 0x04, 0x22, 0x08, 0x22, 0x1c, 0x22, 0x08, 0x22, 0x10, 0x00, 0x00, // 147 0x8b, 0x1a, 0x24, 0x42, 0x08, 0x92, 0x20, 0x84, 0x48, 0xb0, 0x00, 0x00, // 148 0x8a, 0x0c, 0x11, 0x02, 0x2c, 0x12, 0x20, 0x44, 0x18, 0x00, 0x00, 0x00, // 149 0xa9, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // 150 0x8b, 0x02, 0x04, 0x08, 0x14, 0x80, 0x24, 0x00, 0x3e, 0x80, 0x00, 0x00, // 151 0x8b, 0x0c, 0x12, 0x00, 0xa2, 0x00, 0x22, 0x00, 0xa4, 0x18, 0x00, 0x00, // 152 0x8b, 0x0c, 0x32, 0x00, 0x82, 0x00, 0x02, 0x00, 0x8c, 0x30, 0x00, 0x00, // 153 0x8a, 0x04, 0x0a, 0x20, 0x8a, 0x20, 0x0a, 0x24, 0x9a, 0x00, 0x00, 0x00, // 154 0x8a, 0x0c, 0x12, 0x00, 0xa0, 0x02, 0x00, 0x24, 0x98, 0x00, 0x00, 0x00, // 155 0x8b, 0x0c, 0x32, 0x80, 0x02, 0x00, 0x02, 0x0c, 0xb2, 0x00, 0x00, 0x00, // 156 0x8b, 0x06, 0x18, 0x22, 0x08, 0x22, 0x48, 0x22, 0x80, 0x20, 0x00, 0x00, // 157 0x8a, 0x0c, 0x12, 0x28, 0x02, 0x68, 0x02, 0xa8, 0x10, 0x00, 0x00, 0x00, // 158 0x8b, 0x08, 0x20, 0x88, 0x66, 0x18, 0x20, 0x48, 0x20, 0x80, 0x00, 0x00, // 159 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 160 0x9a, 0x00, 0x02, 0x00, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, // 161 0x9a, 0x00, 0x20, 0x40, 0x80, 0x00, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, // 162 0x8b, 0x28, 0x06, 0x38, 0xc0, 0x28, 0x06, 0x38, 0xc0, 0x28, 0x00, 0x00, // 163 0x8a, 0x00, 0x24, 0x10, 0x46, 0x38, 0xc4, 0x10, 0x48, 0x00, 0x00, 0x00, // 164 0x8b, 0x40, 0x82, 0x44, 0x88, 0x10, 0x22, 0x44, 0x82, 0x04, 0x00, 0x00, // 165 0x8b, 0x0c, 0x10, 0x42, 0xa0, 0x12, 0xa8, 0x44, 0x0a, 0x10, 0x00, 0x00, // 166 0xc8, 0x00, 0x00, 0x00, 0x00, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, // 167 0xba, 0x00, 0x00, 0x00, 0x1c, 0x22, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, // 168 0x98, 0x00, 0x02, 0x00, 0x04, 0x88, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, // 169 0x8b, 0x10, 0x04, 0x50, 0x28, 0x10, 0x28, 0x14, 0x40, 0x10, 0x00, 0x00, // 170 0x8b, 0x10, 0x00, 0x14, 0x08, 0x10, 0x20, 0x50, 0x00, 0x10, 0x00, 0x00, // 171 0x29, 0x00, 0x00, 0x01, 0x04, 0x0a, 0x04, 0x08, 0x00, 0x00, 0x00, 0x00, // 172 0x8b, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00, // 173 0xa8, 0x00, 0x00, 0x02, 0x04, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, // 174 0x9a, 0x00, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, // 175 0x8b, 0x1c, 0x20, 0x42, 0x00, 0x82, 0x00, 0x84, 0x08, 0x70, 0x00, 0x00, // 176 0x99, 0x00, 0x02, 0x00, 0x46, 0x18, 0x62, 0x80, 0x00, 0x00, 0x00, 0x00, // 177 0x8b, 0x02, 0x40, 0x06, 0x80, 0x0a, 0x80, 0x12, 0x80, 0x60, 0x00, 0x00, // 178 0x8b, 0x04, 0x00, 0x82, 0x00, 0x92, 0x00, 0xb2, 0x4c, 0x80, 0x00, 0x00, // 179 0x8b, 0x08, 0x10, 0x08, 0x20, 0x08, 0x46, 0x38, 0xc0, 0x08, 0x00, 0x00, // 180 0x8b, 0x04, 0x60, 0x82, 0x20, 0x82, 0x20, 0x84, 0x18, 0x80, 0x00, 0x00, // 181 0x8a, 0x0c, 0x10, 0x22, 0x10, 0x42, 0x10, 0x82, 0x0c, 0x00, 0x00, 0x00, // 182 0x8b, 0x80, 0x02, 0x84, 0x08, 0x90, 0x20, 0x80, 0x40, 0x80, 0x00, 0x00, // 183 0x8b, 0x0c, 0x62, 0x10, 0x82, 0x10, 0x82, 0x10, 0x8c, 0x60, 0x00, 0x00, // 184 0x8a, 0x60, 0x02, 0x90, 0x04, 0x90, 0x08, 0x90, 0x60, 0x00, 0x00, 0x00, // 185 0xa9, 0x00, 0x00, 0x02, 0x14, 0x22, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, // 186 0x2a, 0x00, 0x00, 0x01, 0x04, 0x2a, 0x44, 0x28, 0x40, 0x00, 0x00, 0x00, // 187 0x9a, 0x00, 0x10, 0x08, 0x24, 0x02, 0x40, 0x00, 0x80, 0x00, 0x00, 0x00, // 188 0x8a, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x08, 0x20, 0x00, 0x00, 0x00, // 189 0x9a, 0x00, 0x02, 0x00, 0x04, 0x80, 0x48, 0x20, 0x10, 0x00, 0x00, 0x00, // 190 0x8a, 0x48, 0x02, 0x80, 0x08, 0x80, 0x10, 0x80, 0x60, 0x00, 0x00, 0x00, // 191 0x8b, 0x1c, 0x20, 0x42, 0x80, 0x12, 0x88, 0x22, 0x88, 0x70, 0x00, 0x00, // 192 0x8b, 0x02, 0x04, 0x08, 0x10, 0x28, 0x40, 0x88, 0x00, 0xfe, 0x00, 0x00, // 193 0x8b, 0x06, 0x98, 0x62, 0x80, 0x12, 0x80, 0x12, 0x8c, 0x60, 0x00, 0x00, // 194 0x8b, 0x1c, 0x22, 0x40, 0x82, 0x00, 0x82, 0x00, 0x84, 0x40, 0x00, 0x00, // 195 0x8b, 0x06, 0x98, 0x62, 0x80, 0x02, 0x80, 0x04, 0x88, 0x70, 0x00, 0x00, // 196 0x8b, 0x06, 0x38, 0xc2, 0x10, 0x82, 0x10, 0x82, 0x00, 0x80, 0x00, 0x00, // 197 0x8b, 0x06, 0x38, 0xc0, 0x10, 0x80, 0x10, 0x80, 0x00, 0x80, 0x00, 0x00, // 198 0x8b, 0x1c, 0x22, 0x40, 0x82, 0x00, 0x92, 0x04, 0x98, 0x40, 0x00, 0x00, // 199 0x8b, 0x06, 0x38, 0xc0, 0x10, 0x00, 0x10, 0x06, 0x38, 0xc0, 0x00, 0x00, // 200 0x92, 0x00, 0x02, 0x00, 0x86, 0x18, 0xe2, 0x00, 0x80, 0x00, 0x00, 0x00, // 201 0x8b, 0x0c, 0x02, 0x00, 0x02, 0x80, 0x04, 0x98, 0x60, 0x80, 0x00, 0x00, // 202 0x8b, 0x06, 0x38, 0xc0, 0x10, 0x20, 0x08, 0x44, 0x02, 0x80, 0x00, 0x00, // 203 0x9a, 0x00, 0x06, 0x18, 0x62, 0x80, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, // 204 0x8b, 0x06, 0x38, 0xc0, 0x00, 0x30, 0x00, 0x46, 0x38, 0xc0, 0x00, 0x00, // 205 0x8b, 0x06, 0x38, 0xc0, 0x20, 0x10, 0x08, 0x06, 0x38, 0xc0, 0x00, 0x00, // 206 0x8b, 0x0c, 0x32, 0x40, 0x82, 0x00, 0x82, 0x04, 0x98, 0x60, 0x00, 0x00, // 207 0x8b, 0x06, 0x18, 0x60, 0x90, 0x00, 0x90, 0x00, 0x90, 0x60, 0x00, 0x00, // 208 0x8b, 0x1c, 0x20, 0x42, 0x00, 0x8a, 0x00, 0x84, 0x0a, 0x70, 0x00, 0x00, // 209 0x8b, 0x06, 0x18, 0x60, 0x80, 0x10, 0x88, 0x14, 0x82, 0x60, 0x00, 0x00, // 210 0x8b, 0x04, 0x62, 0x00, 0x92, 0x00, 0x92, 0x00, 0x8c, 0x40, 0x00, 0x00, // 211 0x8b, 0x80, 0x00, 0x86, 0x18, 0xe0, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00, // 212 0x8b, 0x0c, 0x32, 0xc0, 0x02, 0x00, 0x02, 0x0c, 0x30, 0xc0, 0x00, 0x00, // 213 0x9b, 0x00, 0xfe, 0x00, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x00, 0x00, // 214 0x8b, 0x06, 0x38, 0xc4, 0x08, 0x10, 0x08, 0x06, 0x38, 0xc0, 0x00, 0x00, // 215 0x8b, 0x02, 0x84, 0x48, 0x20, 0x18, 0x24, 0x02, 0x40, 0x80, 0x00, 0x00, // 216 0x8b, 0x80, 0x40, 0x26, 0x18, 0x00, 0x20, 0x00, 0x40, 0x80, 0x00, 0x00, // 217 0x8b, 0x02, 0x04, 0x8a, 0x00, 0x92, 0x00, 0xa2, 0x40, 0x80, 0x00, 0x00, // 218 0x9b, 0x00, 0x06, 0x18, 0x62, 0x80, 0x02, 0x80, 0x00, 0x80, 0x00, 0x00, // 219 0xa8, 0x00, 0x00, 0xc0, 0x30, 0x0c, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // 220 0x8a, 0x02, 0x00, 0x02, 0x80, 0x06, 0x98, 0x60, 0x80, 0x00, 0x00, 0x00, // 221 0x9a, 0x00, 0x20, 0x00, 0x40, 0x00, 0x80, 0x40, 0x20, 0x00, 0x00, 0x00, // 222 0x0b, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, // 223 0xb7, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 224 0x8a, 0x04, 0x0a, 0x20, 0x0a, 0x20, 0x0a, 0x24, 0x1a, 0x00, 0x00, 0x00, // 225 0x8a, 0x06, 0x18, 0xe2, 0x00, 0x22, 0x00, 0x24, 0x18, 0x00, 0x00, 0x00, // 226 0x8a, 0x0c, 0x10, 0x02, 0x20, 0x02, 0x20, 0x02, 0x20, 0x00, 0x00, 0x00, // 227 0x8b, 0x0c, 0x10, 0x02, 0x20, 0x02, 0x20, 0x06, 0x38, 0xc0, 0x00, 0x00, // 228 0x8a, 0x0c, 0x12, 0x28, 0x02, 0x28, 0x02, 0x28, 0x10, 0x00, 0x00, 0x00, // 229 0x8b, 0x20, 0x00, 0x26, 0x18, 0x60, 0x00, 0xa0, 0x00, 0x80, 0x00, 0x00, // 230 0x1b, 0x00, 0x18, 0x25, 0x00, 0x45, 0x00, 0x46, 0x18, 0x60, 0x00, 0x00, // 231 0x8a, 0x06, 0x18, 0xe0, 0x00, 0x20, 0x00, 0x26, 0x18, 0x00, 0x00, 0x00, // 232 0x99, 0x00, 0x02, 0x00, 0x26, 0x18, 0x22, 0x80, 0x00, 0x00, 0x00, 0x00, // 233 0x89, 0x01, 0x00, 0x01, 0x00, 0x26, 0x18, 0xa0, 0x00, 0x00, 0x00, 0x00, // 234 0x8a, 0x06, 0x18, 0x60, 0x88, 0x04, 0x12, 0x00, 0x20, 0x00, 0x00, 0x00, // 235 0x99, 0x00, 0x02, 0x00, 0x06, 0x98, 0x62, 0x80, 0x00, 0x00, 0x00, 0x00, // 236 0x8a, 0x26, 0x18, 0x20, 0x06, 0x38, 0x00, 0x26, 0x18, 0x00, 0x00, 0x00, // 237 0x89, 0x26, 0x18, 0x20, 0x00, 0x20, 0x06, 0x18, 0x00, 0x00, 0x00, 0x00, // 238 0x8a, 0x0c, 0x12, 0x00, 0x20, 0x02, 0x00, 0x24, 0x18, 0x00, 0x00, 0x00, // 239 0x0a, 0x03, 0x1c, 0x60, 0x04, 0x40, 0x04, 0x48, 0x30, 0x00, 0x00, 0x00, // 240 0x1b, 0x00, 0x18, 0x24, 0x00, 0x44, 0x00, 0x47, 0x18, 0x60, 0x00, 0x00, // 241 0x89, 0x06, 0x38, 0x00, 0x10, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, // 242 0x8a, 0x02, 0x10, 0x02, 0x28, 0x02, 0x28, 0x04, 0x20, 0x00, 0x00, 0x00, // 243 0x9a, 0x00, 0x20, 0x0c, 0x32, 0xc0, 0x22, 0x00, 0x20, 0x00, 0x00, 0x00, // 244 0x8a, 0x0c, 0x32, 0x00, 0x02, 0x00, 0x02, 0x0c, 0x32, 0x00, 0x00, 0x00, // 245 0x9a, 0x00, 0x3e, 0x00, 0x04, 0x00, 0x08, 0x10, 0x20, 0x00, 0x00, 0x00, // 246 0x8b, 0x0e, 0x30, 0x04, 0x00, 0x18, 0x04, 0x00, 0x06, 0x38, 0x00, 0x00, // 247 0x8b, 0x02, 0x00, 0x24, 0x10, 0x08, 0x04, 0x12, 0x00, 0x20, 0x00, 0x00, // 248 0x1b, 0x00, 0x40, 0x21, 0x12, 0x04, 0x08, 0x10, 0x20, 0x40, 0x00, 0x00, // 249 0x8b, 0x02, 0x00, 0x26, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x20, 0x00, 0x00, // 250 0x9a, 0x00, 0x10, 0x04, 0x1a, 0x60, 0x82, 0x00, 0x80, 0x00, 0x00, 0x00, // 251 0x99, 0x00, 0x02, 0x04, 0x08, 0x20, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, // 252 0x9a, 0x00, 0x02, 0x00, 0x82, 0x0c, 0xb0, 0x40, 0x10, 0x00, 0x00, 0x00, // 253 0x8b, 0x40, 0x80, 0x00, 0x80, 0x40, 0x20, 0x00, 0x20, 0x40, 0x00, 0x00, // 254 0x8b, 0x1a, 0x24, 0x42, 0x08, 0x92, 0x20, 0x84, 0x48, 0xb0, 0x00, 0x00 // 255 }; ImagePrinterEpson::ImagePrinterEpson(MSXMotherBoard& motherBoard) : ImagePrinter(motherBoard, false) { resetEmulatedPrinter(); } const string& ImagePrinterEpson::getName() const { static const string name("epson-printer"); return name; } string_ref ImagePrinterEpson::getDescription() const { return "Emulate Epson FX80 printer, prints to image."; } void ImagePrinterEpson::getNumberOfDots(unsigned& dotsX, unsigned& dotsY) { dotsX = 610; dotsY = 825; } void ImagePrinterEpson::resetSettings() { lineFeed = 12.0; leftBorder = 48; rightBorder = leftBorder + 480; graphDensity = 1.0; fontDensity = 1.0; pageTop = 48; lines = 72; fontWidth = 6; eightBit = -1; memcpy(fontInfo.rom, EpsonFontRom, sizeof(EpsonFontRom)); fontInfo.charWidth = 12; fontInfo.pixelDelta = 0.5; fontInfo.useRam = false; } unsigned ImagePrinterEpson::calcEscSequenceLength(byte character) { switch (character & 127) { case '!': case '-': case '/': case '3': case 'A': case 'J': case 'N': case 'Q': case 'R': case 'S': case 'U': case 'W': case 'b': case 'i': case 'j': case 'l': case 'p': case 's': return 1; case '%': case '?': case 'K': case 'L': case 'Z': case '^': return 2; case '*': case ':': case '&': return 3; case 'B': // Set tabs, variable length (up to 16 tabs) return 0; case 'C': // Set form length, variable length (2 or 3) return 0; case 'D': // Set tabs, variable length (up to 32 tabs) return 0; default: return 0; } } unsigned ImagePrinterEpson::parseNumber(unsigned sizeStart, unsigned sizeChars) { unsigned Value = 0; sizeStart += sizeChars; while (sizeChars--) { Value = Value * 256 + abEscSeq[--sizeStart]; } return Value; } void ImagePrinterEpson::processEscSequence() { byte character = abEscSeq[0] & 127; switch (character) { case '!': { // Master Print Mode Select unsigned masterSelect = parseNumber(1, 1); elite = (masterSelect & 1) != 0; compressed = (masterSelect & 4) != 0; bold = (masterSelect & 8) != 0; doubleStrike = (masterSelect & 16) != 0; doubleWidth = (masterSelect & 32) != 0; if (elite) { fontDensity = 1.20; } else if (compressed) { fontDensity = 1.72; } else { fontDensity = 1.00; } break; } case '#': // Accept Eight Bit as-is break; case '%': // Activates Character Set fontInfo.useRam = parseNumber(1, 1) & 1; break; case '&': // Custom character set, variable length ramLoadOffset = 12 * parseNumber(2, 1); ramLoadEnd = 12 * parseNumber(3, 1) + 12; if (ramLoadEnd <= ramLoadOffset) { ramLoadEnd = ramLoadOffset; } break; case '*': // Turn Graphics Mode ON ninePinGraphics = false; switch (parseNumber(1, 1)) { default: case 0: graphDensity = 1.0; break; case 1: case 2: graphDensity = 2.0; break; case 3: graphDensity = 4.0; break; case 4: graphDensity = 1.33; break; case 5: graphDensity = 1.2; break; case 6: graphDensity = 1.5; break; } sizeRemainingDataBytes = parseNumber(2, 2); break; case '-': // Turn Underline Mode ON/OFF underline = parseNumber(1, 1) != 0; break; case '/': // Selects Vertical Tab Channel break; case '0': // Sets Line Spacing to 1/8 inch lineFeed = 9.0; break; case '1': // Sets Line Spacing to 7/72 inch lineFeed = 7.0; break; case '2': // Sets Line Spacing to 1/6 inch lineFeed = 12.0; break; case '3': // Sets Line Spacing to n/216 inch lineFeed = (parseNumber(1, 1) & 127) / 3.0; break; case '4': // Turn Italic Mode ON italic = true; break; case '5': // Turn Italic Mode OFF italic = false; break; case '6': // Turn Printing of International Italic characters ON noHighEscapeCodes = true; break; case '7': // Turn Printing of International Italic characters OFF noHighEscapeCodes = false; break; case '8': // Turn Paper Out Sensor ON detectPaperOut = true; break; case '9': // Turn Paper Out Sensor OFF detectPaperOut = false; break; case ':': // Copies Rom Character set to RAM memcpy(fontInfo.ram, fontInfo.rom, sizeof(fontInfo.ram)); break; case '<': // Turn Uni-directional printing ON (left to right) leftToRight = true; break; case '=': // Sets eight bit to 0 eightBit = 0; break; case '>': // Sets eight bit to 1 eightBit = 1; break; case '?': // Redefines Graphics Codes break; case '@': // Reset eightBit = -1; ninePinGraphics = false; graphDensity = 1.0; fontDensity = 1.0; underline = false; lineFeed = 12.0; italic = false; detectPaperOut = false; leftToRight = false; doubleStrike = false; elite = false; compressed = false; rightBorder = 6 * 78; subscript = false; superscript = false; doubleWidth = false; bold = false; proportional = false; fontInfo.useRam = false; noHighEscapeCodes = false; alternateChar = false; countryCode = CC_USA; break; case 'A': // Sets Line Spacing to n/72 inch lineFeed = parseNumber(1, 1) & 127; break; case 'B': // Set tabs, variable length (up to 16 tabs) break; case 'C': // Set form length, variable length (2 or 3) break; case 'D': // Set tabs, variable length (up to 32 tabs) break; case 'E': // Turn Emphasized Mode ON bold = true; break; case 'F': // Turn Emphasized Mode OFF bold = false; break; case 'G': // Turn Double Strike Mode ON doubleStrike = true; break; case 'H': // Turn Double Strike Mode OFF doubleStrike = false; break; case 'I': // Enables printing of chars 1-31 alternateChar = parseNumber(1, 1) & 1; break; case 'J': // Forces Line Feed with n/216 inch vpos += (parseNumber(1, 1) & 127) / 3.0; if (vpos >= pageHeight) { flushEmulatedPrinter(); } break; case 'K': // Turn Single Density Graphics on (480 dot mode) graphDensity = 1.0; ninePinGraphics = false; sizeRemainingDataBytes = parseNumber(1, 2); break; case 'L': // Turn Double Density Graphics on (960 dot mode) graphDensity = 2.0; ninePinGraphics = false; sizeRemainingDataBytes = parseNumber(1, 2); break; case 'M': // Turn Elite mode ON elite = true; fontDensity = 1.20; break; case 'N': // Turn Skip Over Perforation ON break; case 'O': // Turn Skip Over Perforation OFF break; case 'P': // Turn Elite mode OFF elite = false; fontDensity = compressed ? 1.72 : 1.00; break; case 'Q': { // Set Right Margin int width = parseNumber(1, 2); if (width > 78) width = 78; // FIXME Font dependent !! rightBorder = 6 * width; break; } case 'R': // Select International Character Set countryCode = static_cast(parseNumber(1, 1)); if (countryCode > CC_JAPAN) { countryCode = CC_USA; } break; case 'S': { // Turn Script Mode ON int script = parseNumber(1, 1) & 1; superscript = script == 0; subscript = script == 1; break; } case 'T': // Turn Script Mode OFF subscript = false; superscript = false; break; case 'U': // Turn Uni-directional mode ON/OFF leftToRight = parseNumber(1, 1) != 0; break; case 'W': // Turn Expanded Mode ON/OFF normalAfterLine = false; doubleWidth = parseNumber(1, 1) != 0; break; case 'Y': // Turn High Speed Double Density Graphics ON break; case 'Z': // Turns Quadruple Density Graphics ON graphDensity = 4.0; ninePinGraphics = false; sizeRemainingDataBytes = parseNumber(1, 2); break; case '^': // Turn Nine Pin Graphics Mode ON graphDensity = parseNumber(1, 1) ? 2.0 : 1.0; ninePinGraphics = true; sizeRemainingDataBytes = 2 * parseNumber(2, 2); break; case 'b': // Set Vertical Tab break; case 'i': // Turn Immediate Mode ON/OFF break; case 'j': // Immediate Reverse Line Feed vpos -= (parseNumber(1, 1) & 127) / 3.0; if (vpos < pageTop) { vpos = pageTop; } break; case 'l': // Set Left Margin break; case 'p': // Turn proportional mode ON/OFF proportional = parseNumber(1, 1) != 0; break; case 's': // Set Print Speed break; case 127: // Deletes Last Character in Buffer break; } } // International character code translation for the Epson FX-80 printer // US FR DE GB DK SE IT SP JP static byte intlChar35 [9] = { 35, 35, 35, 6, 35, 35, 35, 12, 35 }; static byte intlChar36 [9] = { 36, 36, 36, 36, 36, 11, 36, 36, 36 }; static byte intlChar64 [9] = { 64, 0, 16, 64, 64, 29, 64, 64, 64 }; static byte intlChar91 [9] = { 91, 5, 23, 91, 18, 23, 5, 7, 91 }; static byte intlChar92 [9] = { 92, 15, 24, 92, 20, 24, 92, 9, 31 }; static byte intlChar93 [9] = { 93, 16, 25, 93, 13, 13, 30, 8, 93 }; static byte intlChar94 [9] = { 94, 94, 94, 94, 94, 25, 94, 94, 94 }; static byte intlChar96 [9] = { 96, 96, 96, 96, 96, 30, 2, 96, 96 }; static byte intlChar123[9] = { 123, 30, 26, 123, 19, 26, 0, 22, 123 }; static byte intlChar124[9] = { 124, 2, 27, 124, 21, 27, 3, 10, 124 }; static byte intlChar125[9] = { 125, 1, 28, 125, 14, 14, 1, 125, 125 }; static byte intlChar126[9] = { 126, 22, 17, 126, 126, 28, 4, 126, 126 }; void ImagePrinterEpson::processCharacter(byte data) { if (data >= 32) { if (italic) { data |= 128; } else { data &= 127; } } if (!noHighEscapeCodes && data >= 128 && data < 160) { data &= 31; } // Convert international characters switch (data & 0x7f) { case 35: data = (data & 0x80) | intlChar35 [countryCode]; break; case 36: data = (data & 0x80) | intlChar36 [countryCode]; break; case 64: data = (data & 0x80) | intlChar64 [countryCode]; break; case 91: data = (data & 0x80) | intlChar91 [countryCode]; break; case 92: data = (data & 0x80) | intlChar92 [countryCode]; break; case 93: data = (data & 0x80) | intlChar93 [countryCode]; break; case 94: data = (data & 0x80) | intlChar94 [countryCode]; break; case 96: data = (data & 0x80) | intlChar96 [countryCode]; break; case 123: data = (data & 0x80) | intlChar123[countryCode]; break; case 124: data = (data & 0x80) | intlChar124[countryCode]; break; case 125: data = (data & 0x80) | intlChar125[countryCode]; break; case 126: data = (data & 0x80) | intlChar126[countryCode]; break; } if (data >= 32) { printVisibleCharacter(data); return; } switch (data) { case 0: // Terminates horizontal and vertical TAB setting break; case 7: // Sound beeper break; case 8: // Backspace // TODO: fix for other font-sizes hpos -= 8; if (hpos < leftBorder) { hpos = leftBorder; } break; case 9: { // Horizontal TAB // TODO: fix for other font-sizes hpos = ((unsigned(hpos) + 64 - leftBorder) & ~63) + leftBorder; if (hpos < rightBorder) { break; } hpos = leftBorder; // fall thru: CR/LF } case 10: // Line Feed case 11: // Vertical TAB vpos += lineFeed; if (vpos >= pageHeight) { flushEmulatedPrinter(); } break; case 12: // Form Feed ensurePrintPage(); flushEmulatedPrinter(); break; case 13: // Carrige return hpos = leftBorder; break; case 14: // Turns expanded mode ON doubleWidth = true; normalAfterLine = true; break; case 15: // Shift in. Emties buffer, turns compressed mode ON (17.16 cpi) compressed = true; if (!elite) { fontDensity = 1.72; } break; case 17: // Device Control 1: break; case 18: // Device Control 2: turns compressed mode OFF compressed = false; fontDensity = 1.00; break; case 19: // Device Control 3: break; case 20: // Device Control 4: Turns expanded mode OFF doubleWidth = false; break; case 24: // Cancels all text in the print buffer break; case 27: // Escape escSequence = true; break; default: if (alternateChar) { printVisibleCharacter(data); } break; } } template void ImagePrinterEpson::serialize(Archive& /*ar*/, unsigned /*version*/) { // TODO is this worth it? } INSTANTIATE_SERIALIZE_METHODS(ImagePrinterEpson); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, ImagePrinterEpson, "ImagePrinterEpson"); // class Paper Paper::Paper(unsigned x, unsigned y, double dotSizeX, double dotSizeY) : buf(x * y) , sizeX(x), sizeY(y) { memset(buf.data(), 255, x * y); setDotSize(dotSizeX, dotSizeY); } Paper::~Paper() { } string Paper::save() const { string filename = FileOperations::getNextNumberedFileName( "prints", "page", ".png"); VLA(const void*, rowPointers, sizeY); for (unsigned y = 0; y < sizeY; ++y) { rowPointers[y] = &buf[sizeX * y]; } PNG::saveGrayscale(sizeX, sizeY, rowPointers, filename); return filename; } void Paper::setDotSize(double sizeX, double sizeY) { radiusX = sizeX / 2.0; radiusY = sizeY / 2.0; int rx = int(16 * radiusX); int ry = int(16 * radiusY); radius16 = ry; table.clear(); table.resize(2 * (radius16 + 16), -(1 << 30)); int offset = ry + 16; int rx2 = 2 * rx * rx; int ry2 = 2 * ry * ry; int x = 0; int y = ry; int de_x = ry * ry; int de_y = (1 - 2 * ry) * rx * rx; int e = 0; int sx = 0; int sy = rx2 * ry; while (sx <= sy) { table[offset - y - 1] = x; table[offset + y ] = x; x += 1; sx += ry2; e += de_x; de_x += ry2; if ((2 * e + de_y) > 0) { y -= 1; sy -= rx2; e += de_y; de_y += rx2; } } x = rx; y = 0; de_x = (1 - 2 * rx) * ry * ry; de_y = rx * rx; e = 0; sx = ry2 * rx; sy = 0; while (sy <= sx) { table[offset - y - 1] = x; table[offset + y ] = x; y += 1; sy += rx2; e += de_y; de_y += rx2; if ((2 * e + de_x) > 0) { x -= 1; sx -= ry2; e += de_x; de_x += ry2; } } } void Paper::plot(double xPos, double yPos) { unsigned xx1 = max(int(floor(xPos - radiusX)), 0); unsigned xx2 = min(int(ceil (xPos + radiusX)), sizeX); unsigned yy1 = max(int(floor(yPos - radiusY)), 0); unsigned yy2 = min(int(ceil (yPos + radiusY)), sizeY); int y = 16 * yy1 - int(16 * yPos) + 16 + radius16; for (unsigned yy = yy1; yy < yy2; ++yy) { int x = 16 * xx1 - int(16 * xPos); for (unsigned xx = xx1; xx < xx2; ++xx) { int sum = 0; for (int i = 0; i < 16; ++i) { int a = table[y + i]; if (x < -a) { int t = 16 + a + x; if (t > 0) { sum += min(t, 2 * a); } } else { int t = a - x; if (t > 0) { sum += min(16, t); } } } dot(xx, yy) = max(0, dot(xx, yy) - sum); x += 16; } y += 16; } } byte& Paper::dot(unsigned x, unsigned y) { assert(x < sizeX); assert(y < sizeY); return buf[y * sizeX + x]; } } // namespace openmsx openmsx-0.10.0/src/Pluggable.cc0000644000175000017500000000170612262345041017050 0ustar manuelmanuel00000000000000#include "Pluggable.hh" #include "PlugException.hh" #include "Connector.hh" #include "unreachable.hh" #include using std::string; namespace openmsx { Pluggable::Pluggable() { setConnector(nullptr); } Pluggable::~Pluggable() { } const string& Pluggable::getName() const { static const string name("--empty--"); return name; } void Pluggable::plug(Connector& newConnector, EmuTime::param time) { assert(getClass() == newConnector.getClass()); if (connector) { throw PlugException(getName() + " already plugged in " + connector->getName() + '.'); } plugHelper(newConnector, time); setConnector(&newConnector); } void Pluggable::unplug(EmuTime::param time) { try { unplugHelper(time); } catch (MSXException&) { UNREACHABLE; } setConnector(nullptr); } Connector* Pluggable::getConnector() const { return connector; } void Pluggable::setConnector(Connector* conn) { connector = conn; } } // namespace openmsx openmsx-0.10.0/src/cpu/0000755000175000017500000000000012262345041015422 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/cpu/VDPIODelay.cc0000644000175000017500000000360412262345041017574 0ustar manuelmanuel00000000000000#include "VDPIODelay.hh" #include "MSXCPU.hh" #include "MSXCPUInterface.hh" #include "DeviceConfig.hh" #include "DummyDevice.hh" #include "serialize.hh" #include namespace openmsx { VDPIODelay::VDPIODelay(const DeviceConfig& config, MSXCPUInterface& cpuInterface) : MSXDevice(config) , cpu(getCPU()) // used frequently, so cache it , lastTime(EmuTime::zero) { for (int port = 0x098; port <= 0x9B; ++port) { getInDevicePtr (port) = &cpuInterface.getDummyDevice(); getOutDevicePtr(port) = &cpuInterface.getDummyDevice(); } } const MSXDevice& VDPIODelay::getInDevice(byte port) const { assert((0x98 <= port) && (port <= 0x9B)); return *inDevices[port - 0x98]; } MSXDevice*& VDPIODelay::getInDevicePtr(byte port) { assert((0x98 <= port) && (port <= 0x9B)); return inDevices[port - 0x98]; } MSXDevice*& VDPIODelay::getOutDevicePtr(byte port) { assert((0x98 <= port) && (port <= 0x9B)); return outDevices[port - 0x98]; } byte VDPIODelay::readIO(word port, EmuTime::param time) { delay(time); return getInDevicePtr(byte(port))->readIO(byte(port), lastTime.getTime()); } byte VDPIODelay::peekIO(word port, EmuTime::param time) const { return getInDevice(byte(port)).peekIO(byte(port), time); } void VDPIODelay::writeIO(word port, byte value, EmuTime::param time) { delay(time); getOutDevicePtr(byte(port))->writeIO(byte(port), value, lastTime.getTime()); } void VDPIODelay::delay(EmuTime::param time) { cpu.waitCycles(1); if (cpu.isR800Active()) { // Number of cycles based on measurements on real HW. // See doc/turbor-vdp-io-timing.ods for details. lastTime += 62; // 8us if (time < lastTime.getTime()) { cpu.wait(lastTime.getTime()); return; } } lastTime.advance(time); } template void VDPIODelay::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("lastTime", lastTime); } INSTANTIATE_SERIALIZE_METHODS(VDPIODelay); } // namespace openmsx openmsx-0.10.0/src/cpu/DebugCondition.hh0000644000175000017500000000073612262345041020646 0ustar manuelmanuel00000000000000#ifndef DEBUGCONDITION_HH #define DEBUGCONDITION_HH #include "BreakPointBase.hh" namespace openmsx { /** General debugger condition * Like breakpoints, but not tied to a specifc address. */ class DebugCondition : public BreakPointBase { public: DebugCondition(GlobalCliComm& CliComm, TclObject command, TclObject condition); unsigned getId() const { return id; } private: const unsigned id; static unsigned lastId; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/CPUCore.cc0000644000175000017500000044237012262345041017203 0ustar manuelmanuel00000000000000// MEMORY EMULATION // ---------------- // // Memory access emulation is a very important part of the CPU emulation. // Because they happen so frequently they really need to be executed as fast as // possible otherwise they will completely bring down the speed of the CPU // emulation. // // A very fast way to emulate memory accesses is by simply reading/writing to a // 64kb array (for a 16-bit address space). Unfortunately this doesn't allow // for memory mapped IO (MMIO). These are memory regions where read/writes // trigger side effects, so where we need to execute device-specific code on // read or writes. An alternative that does work with MMIO is for every access // execute a virtual method call, (this is the approach taken by most current // MSX emulators). Unfortunately this is also a lot slower. // // It is possible to combine the speed of array accesses with the flexibility // of virtual methods. In openMSX it's implemened as follows: the 64kb address // space is divided in 256 regions of 256 bytes (called cacheLines in the code // below). For each such region we store a pointer, if this pointer is nullptr // then we have to use the slow way (=virtual method call). If it is not nullptr, // the pointer points to a block of memory that can be directly accessed. In // some contexts accesses via the pointer are known as backdoor accesses while // the accesses directly to the device are known as frontdoor accesses. // // We keep different pointers for read and write accesses. This allows to also // implement ROMs efficiently: read is handled as regular RAM, but writes end // up in some dummy memory region. This region is called 'unmappedWrite' in the // code. There is also a special region 'unmappedRead', this region is filled // with 0xFF and can be used to model (parts of) a device that don't react to // reads (so reads return 0xFF). // // Because of bankswitching (the MSX slot select mechanism, but also e.g. // MegaROM backswitching) the memory map as seen by the Z80 is not static. This // means that the cacheLine pointers also need to change during runtime. To // solve this we made the bankswitch code also responsible for invalidating the // cacheLines of the switched region. These pointers are filled-in again in a // lazy way: the first read or write to a cache line will first get this // pointer (see getReadCacheLine() and getWriteCacheLine() in the code below), // from then on this pointer is used for all further accesses to this region, // until the cache is invalidated again. // // // INSTRUCTION EMULATION // --------------------- // // UPDATE: the 'threaded interpreter model' is not enabled by default // main reason is the huge memory requirement while compiling // and that it doesn't work on non-gcc compilers // // The current implementation is based on a 'threaded interpreter model'. In // the text below I'll call the older implementation the 'traditional // interpreter model'. From a very high level these two models look like this: // // Traditional model: // while (!needExit()) { // byte opcode = fetch(PC++); // switch (opcode) { // case 0x00: nop(); break; // case 0x01: ld_bc_nn(); break; // ... // } // } // // Threaded model: // byte opcode = fetch(PC++); // // goto *(table[opcode]); // fetch-and-dispatch // // note: the goto * syntax is a gcc extension called computed-gotos // // op00: nop(); if (!needExit()) [fetch-and-dispatch]; // op01: ld_bc_nn(); if (!needExit()) [fetch-and-dispatch]; // ... // // In the first model there is a central place in the code that fetches (the // first byte of) the instruction and based on this byte jumps to the // appropriate routine. In the second model, this fetch-and-dispatch logic is // duplicated at the end of each instruction. // // Typically the 'dispatch' part in above paragraph is implemented (either by // the compiler or manually using computed goto's) via a jump table. Thus on // assembler level via an indirect jump. For the host CPU it's hard to predict // the destination address of such an indirect jump, certainly if there's only // one such jump for all dispatching (the traditional model). If each // instruction has its own indirect jump instruction (the threaded model), it // becomes a bit easier, because often one particular z80 instructions is // followed by a specific other z80 instruction (or one from a small subset). // For example a z80 'cp' instruction is most likely followed by a 'conditional // jump' z80 instruction. Modern CPUs are quite sensitive to // branch-(mis)predictions, so the above optimization helps quite a lot. I // measured a speedup of more than 10%! // // There is another advantage to the threaded model. Because also the // needExit() test is duplicated for each instruction, it becomes possible to // tweak it for individual instructions. But first let me explain this // exit-test in more detail. // // These are the main reasons why the emulator should stop emulating CPU // instructions: // 1) When other devices than the CPU must be emulated (e.g. video frame // rendering). In openMSX this is handled by the Scheduler class and // actually we don't exit the CPU loop (anymore) for this. Instead we // simply execute the device code as a subroutine. Each time right before // we access an IO port or do a frontdoor memory access, there is a check // whether we should emulate device code (search for schedule() in the code // below). // 2) To keep the inner CPU loop as fast as possible we don't check for IRQ, // NMI or HALT status in this loop. Instead this condition is checked only // once at the beginning outside of the loop (if there wasn't a pending IRQ // on the first instruction there also won't be one on the second // instruction, if all we did was emulating cpu instructions). Now when one // of these conditions changes, we must exit the inner loop and re-evaluate // them. For example after an EI instruction we must check the IRQ status // again. // 3) Various reasons like: // * Z80/R800 switch // * executing a Tcl command (could be a cpu-register debug read) // * exit the emulator // 4) 'once-in-a-while': To avoid threading problems and race conditions, // several threads in openMSX only 'schedule' work that will later be // executed by the main emulation thread. The main thread checks for such // task outside of the cpu emulation loop. So once-in-a-while we need to // exit the loop. The exact timing doesn't matter here because anyway the // relative timing between threads is undefined. // So for 1) we don't need to do anything (we don't actually exit). For 2) and // 3) we need the exit the loop as soon as possible (right after the current // instruction is finished). For 4) it's OK to exit 'eventually' (a few hundred // z80 instructions late is still OK). // // Condition 2) is implemented with the 'slowInstructions' mechanism. Condition // 3) via exitCPULoopSync() (may only get called by the main emulation thread) // and condition 4) is implemented via exitCPULoopAsync() (can be called from // any thread). // // Now back to the exit-test optimization: in the threaded model each // instruction ends with: // // if (needExit()) return // byte opcode = fetch(PC++); // goto *(table[opcode]); // // And if we look in more detail at fetch(): // // if (canDoBackdoor(addr)) { // doBackdoorAccess(addr); // } else { // doFrontdoorAccess(addr); // } // // So there are in fact two checks per instruction. This can be reduced to only // one check with the following trick: // // !!!WRONG!!! // In the past we optimized this to only check canDoBackdoor() (and make sure // canDoBackdoor() returned false when needExit() would return true). This // worked rather well, except for one case: when we exit the CPU loop we also // check for pending Syncronization points. It is possible such a SyncPoint // raises the IRQ line. So it is important to check for exit after every // instruction, otherwise we would enter the IRQ routine a couple of // instructions too late. #include "CPUCore.hh" #include "MSXCPUInterface.hh" #include "Scheduler.hh" #include "MSXMotherBoard.hh" #include "CliComm.hh" #include "BooleanSetting.hh" #include "IntegerSetting.hh" #include "TclCallback.hh" #include "Dasm.hh" #include "Z80.hh" #include "R800.hh" #include "Thread.hh" #include "endian.hh" #include "likely.hh" #include "inline.hh" #include "unreachable.hh" #include "memory.hh" #include "build-info.hh" #include #include #include #include #include // // #define USE_COMPUTED_GOTO // // Computed goto's are not enabled by default: // - Computed goto's are a gcc extension, it's not part of the official c++ // standard. So this will only work if you use gcc as your compiler (it // won't work with visual c++ for example) // - This is only beneficial on CPUs with branch prediction for indirect jumps // and a reasonable amout of cache. For example it is very benefical for a // intel core2 cpu (10% faster), but not for a ARM920 (a few percent slower) // - Compiling src/cpu/CPUCore.cc with computed goto's enabled is very demanding // on the compiler. On older gcc versions it requires up to 1.5GB of memory. // But even on more recent gcc versions it still requires around 700MB. // // Probably the easiest way to enable this, is to pass the -DUSE_COMPUTED_GOTO // flag to the compiler. This is for example done in the super-opt flavour. // See build/flavour-super-opt.mk using std::string; namespace openmsx { // This actually belongs in Z80.cc and R800.cc (these files don't exist yet). // As a quick hack I put these two lines here because I found it overkill to // create two files each containing only a single line. // Technically these two lines _are_ required according to the c++ standard. // Though usually it works just find without them, but during experiments I did // get a link error when these lines were missing (it only happened during a // debug build with some specific compiler version and only with some // combination of other code changes, but again when strictly following the // language rules, these lines should be here). // ... But visual studio is not fully standard compliant, see also comment // in SectorAccesibleDisk.cc #ifndef _MSC_VER const int Z80TYPE ::CLOCK_FREQ; const int R800TYPE::CLOCK_FREQ; #endif enum Reg8 : int { A, F, B, C, D, E, H, L, IXH, IXL, IYH, IYL, REG_I, REG_R, DUMMY }; enum Reg16 : int { AF, BC, DE, HL, IX, IY, SP }; // flag positions static const byte S_FLAG = 0x80; static const byte Z_FLAG = 0x40; static const byte Y_FLAG = 0x20; static const byte H_FLAG = 0x10; static const byte X_FLAG = 0x08; static const byte V_FLAG = 0x04; static const byte P_FLAG = V_FLAG; static const byte N_FLAG = 0x02; static const byte C_FLAG = 0x01; // flag-register tables, initialized at run-time static byte ZSTable[256]; static byte ZSXYTable[256]; static byte ZSPTable[256]; static byte ZSPXYTable[256]; static byte ZSPHTable[256]; static const byte ZS0 = Z_FLAG; static const byte ZSXY0 = Z_FLAG; static const byte ZSP0 = Z_FLAG | V_FLAG; static const byte ZSPXY0 = Z_FLAG | V_FLAG; static const byte ZS255 = S_FLAG; static const byte ZSXY255 = S_FLAG | X_FLAG | Y_FLAG; typedef signed char offset; // Global variable, because it should be shared between Z80 and R800. // It must not be shared between the CPUs of different MSX machines, but // the (logical) lifetime of this variable cannot overlap between execution // of two MSX machines. static word start_pc; // conditions struct CondC { bool operator()(byte f) const { return (f & C_FLAG) != 0; } }; struct CondNC { bool operator()(byte f) const { return !(f & C_FLAG); } }; struct CondZ { bool operator()(byte f) const { return (f & Z_FLAG) != 0; } }; struct CondNZ { bool operator()(byte f) const { return !(f & Z_FLAG); } }; struct CondM { bool operator()(byte f) const { return (f & S_FLAG) != 0; } }; struct CondP { bool operator()(byte f) const { return !(f & S_FLAG); } }; struct CondPE { bool operator()(byte f) const { return (f & V_FLAG) != 0; } }; struct CondPO { bool operator()(byte f) const { return !(f & V_FLAG); } }; struct CondTrue { bool operator()(byte) const { return true; } }; static void initTables() { static bool alreadyInit = false; if (alreadyInit) return; alreadyInit = true; for (int i = 0; i < 256; ++i) { byte zFlag = (i == 0) ? Z_FLAG : 0; byte sFlag = i & S_FLAG; byte xFlag = i & X_FLAG; byte yFlag = i & Y_FLAG; byte vFlag = V_FLAG; for (int v = 128; v != 0; v >>= 1) { if (i & v) vFlag ^= V_FLAG; } ZSTable [i] = zFlag | sFlag; ZSXYTable [i] = zFlag | sFlag | xFlag | yFlag; ZSPTable [i] = zFlag | sFlag | vFlag; ZSPXYTable[i] = zFlag | sFlag | xFlag | yFlag | vFlag; ZSPHTable [i] = zFlag | sFlag | vFlag | H_FLAG; } assert(ZSTable [ 0] == ZS0); assert(ZSXYTable [ 0] == ZSXY0); assert(ZSPTable [ 0] == ZSP0); assert(ZSPXYTable[ 0] == ZSPXY0); assert(ZSTable [255] == ZS255); assert(ZSXYTable [255] == ZSXY255); } template CPUCore::CPUCore( MSXMotherBoard& motherboard_, const string& name, const BooleanSetting& traceSetting_, TclCallback& diHaltCallback_, EmuTime::param time) : CPURegs(T::isR800()) , T(time, motherboard_.getScheduler()) , motherboard(motherboard_) , scheduler(motherboard.getScheduler()) , interface(nullptr) , traceSetting(traceSetting_) , diHaltCallback(diHaltCallback_) , IRQStatus(motherboard.getDebugger(), name + ".pendingIRQ", "Non-zero if there are pending IRQs (thus CPU would enter " "interrupt routine in EI mode).", 0) , IRQAccept(motherboard.getDebugger(), name + ".acceptIRQ", "This probe is only useful to set a breakpoint on (the value " "return by read is meaningless). The breakpoint gets triggered " "right after the CPU accepted an IRQ.") , freqLocked(make_unique( motherboard.getCommandController(), name + "_freq_locked", "real (locked) or custom (unlocked) " + name + " frequency", true)) , freqValue(make_unique( motherboard.getCommandController(), name + "_freq", "custom " + name + " frequency (only valid when unlocked)", T::CLOCK_FREQ, 1000000, 1000000000)) , freq(T::CLOCK_FREQ) , NMIStatus(0) , nmiEdge(false) , exitLoop(false) , tracingEnabled(traceSetting.getBoolean()) , isTurboR(motherboard.isTurboR()) { static_assert(!std::is_polymorphic>::value, "keep CPUCore non-virtual to keep PC at offset 0"); doSetFreq(); doReset(time); initTables(); } template void CPUCore::setInterface(MSXCPUInterface* interf) { interface = interf; } template void CPUCore::warp(EmuTime::param time) { assert(T::getTimeFast() <= time); T::setTime(time); } template EmuTime::param CPUCore::getCurrentTime() const { return T::getTime(); } template void CPUCore::invalidateMemCache(unsigned start, unsigned size) { unsigned first = start / CacheLine::SIZE; unsigned num = (size + CacheLine::SIZE - 1) / CacheLine::SIZE; memset(&readCacheLine [first], 0, num * sizeof(byte*)); // nullptr memset(&writeCacheLine [first], 0, num * sizeof(byte*)); // memset(&readCacheTried [first], 0, num * sizeof(bool)); // FALSE memset(&writeCacheTried[first], 0, num * sizeof(bool)); // } template void CPUCore::doReset(EmuTime::param time) { // AF and SP are 0xFFFF // PC, R, IFF1, IFF2, HALT and IM are 0x0 // all others are random setAF(0xFFFF); setBC(0xFFFF); setDE(0xFFFF); setHL(0xFFFF); setIX(0xFFFF); setIY(0xFFFF); setPC(0x0000); setSP(0xFFFF); setAF2(0xFFFF); setBC2(0xFFFF); setDE2(0xFFFF); setHL2(0xFFFF); clearNextAfter(); copyNextAfter(); setIFF1(false); setIFF2(false); setHALT(false); setExtHALT(false); setIM(0); setI(0x00); setR(0x00); T::setMemPtr(0xFFFF); invalidateMemCache(0x0000, 0x10000); // We expect this assert to be valid // assert(T::getTimeFast() <= time); // time shouldn't go backwards // But it's disabled for the following reason: // 'motion' (IRC nickname) managed to create a replay file that // contains a reset command that falls in the middle of a Z80 // instruction. Replayed commands go via the Scheduler, and are // (typically) executed right after a complete CPU instruction. So // the CPU is (slightly) ahead in time of the about to be executed // reset command. // Normally this situation should never occur: console commands, // hotkeys, commands over clicomm, ... are all handled via the global // event mechanism. Such global events are scheduled between CPU // instructions, so also in a replay they should fall between CPU // instructions. // However if for some reason the timing of the emulation changed // (improved emulation accuracy or a bug so that emulation isn't // deterministic or the replay file was edited, ...), then the above // reasoning no longer holds and the assert can trigger. // We need to be robust against loading older replays (when emulation // timing has changed). So in that respect disabling the assert is // good. Though in the example above (motion's replay) it's not clear // whether the assert is really triggered by mixing an old replay // with a newer openMSX version. In any case so far we haven't been // able to reproduce this assert by recording and replaying using a // single openMSX version. T::setTime(time); assert(NMIStatus == 0); // other devices must reset their NMI source assert(IRQStatus == 0); // other devices must reset their IRQ source } // I believe the following two methods are thread safe even without any // locking. The worst that can happen is that we occasionally needlessly // exit the CPU loop, but that's harmless // TODO thread issues are always tricky, can someone confirm this really // is thread safe template void CPUCore::exitCPULoopAsync() { // can get called from non-main threads exitLoop = true; } template void CPUCore::exitCPULoopSync() { assert(Thread::isMainThread()); exitLoop = true; T::disableLimit(); } template inline bool CPUCore::needExitCPULoop() { // always executed in main thread if (unlikely(exitLoop)) { exitLoop = false; return true; } return false; } template void CPUCore::setSlowInstructions() { slowInstructions = 2; T::disableLimit(); } template void CPUCore::raiseIRQ() { assert(IRQStatus >= 0); if (IRQStatus == 0) { setSlowInstructions(); } IRQStatus = IRQStatus + 1; } template void CPUCore::lowerIRQ() { IRQStatus = IRQStatus - 1; assert(IRQStatus >= 0); } template void CPUCore::raiseNMI() { // NMIs are currently disabled, they are anyway not used in MSX and // not having to check for them allows to emulate slightly faster UNREACHABLE; assert(NMIStatus >= 0); if (NMIStatus == 0) { nmiEdge = true; setSlowInstructions(); } NMIStatus++; } template void CPUCore::lowerNMI() { NMIStatus--; assert(NMIStatus >= 0); } template bool CPUCore::isM1Cycle(unsigned address) const { // PC was already increased, so decrease again return address == ((getPC() - 1) & 0xFFFF); } template void CPUCore::wait(EmuTime::param time) { assert(time >= getCurrentTime()); scheduler.schedule(time); T::advanceTime(time); } template void CPUCore::waitCycles(unsigned cycles) { T::add(cycles); } template void CPUCore::setNextSyncPoint(EmuTime::param time) { T::setLimit(time); } static inline char toHex(byte x) { return (x < 10) ? (x + '0') : (x - 10 + 'A'); } static void toHex(byte x, char* buf) { buf[0] = toHex(x / 16); buf[1] = toHex(x & 15); } template void CPUCore::disasmCommand( const std::vector& tokens, TclObject& result) const { word address = (tokens.size() < 3) ? getPC() : tokens[2].getInt(); byte outBuf[4]; std::string dasmOutput; unsigned len = dasm(*interface, address, outBuf, dasmOutput, T::getTimeFast()); result.addListElement(dasmOutput); char tmp[3]; tmp[2] = 0; for (unsigned i = 0; i < len; ++i) { toHex(outBuf[i], tmp); result.addListElement(tmp); } } template void CPUCore::update(const Setting& setting) { if (&setting == freqLocked.get()) { doSetFreq(); } else if (&setting == freqValue.get()) { doSetFreq(); } else if (&setting == &traceSetting) { tracingEnabled = traceSetting.getBoolean(); } } template void CPUCore::setFreq(unsigned freq_) { freq = freq_; doSetFreq(); } template void CPUCore::doSetFreq() { if (freqLocked->getBoolean()) { // locked, use value set via setFreq() T::setFreq(freq); } else { // unlocked, use value set by user T::setFreq(freqValue->getInt()); } } template inline byte CPUCore::READ_PORT(unsigned port, unsigned cc) { EmuTime time = T::getTimeFast(cc); scheduler.schedule(time); byte result = interface->readIO(port, time); // note: no forced page-break after IO return result; } template inline void CPUCore::WRITE_PORT(unsigned port, byte value, unsigned cc) { EmuTime time = T::getTimeFast(cc); scheduler.schedule(time); interface->writeIO(port, value, time); // note: no forced page-break after IO } template template NEVER_INLINE byte CPUCore::RDMEMslow(unsigned address, unsigned cc) { // not cached unsigned high = address >> CacheLine::BITS; if (!readCacheTried[high]) { // try to cache now unsigned addrBase = address & CacheLine::HIGH; if (const byte* line = interface->getReadCacheLine(addrBase)) { // cached ok T::template PRE_MEM(address); T::template POST_MEM< POST_PB>(address); readCacheLine[high] = line - addrBase; return readCacheLine[high][address]; } } // uncacheable readCacheTried[high] = true; T::template PRE_MEM(address); EmuTime time = T::getTimeFast(cc); scheduler.schedule(time); byte result = interface->readMem(address, time); T::template POST_MEM(address); return result; } template template ALWAYS_INLINE byte CPUCore::RDMEM_impl2(unsigned address, unsigned cc) { const byte* line = readCacheLine[address >> CacheLine::BITS]; if (likely(line != nullptr)) { // cached, fast path T::template PRE_MEM(address); T::template POST_MEM< POST_PB>(address); return line[address]; } else { return RDMEMslow(address, cc); // not inlined } } template template ALWAYS_INLINE byte CPUCore::RDMEM_impl(unsigned address, unsigned cc) { static const bool PRE = T::template Normalize::value; static const bool POST = T::template Normalize::value; return RDMEM_impl2(address, cc); } template ALWAYS_INLINE byte CPUCore::RDMEM_OPCODE(unsigned cc) { unsigned address = getPC(); setPC(address + 1); return RDMEM_impl(address, cc); } template ALWAYS_INLINE byte CPUCore::RDMEM(unsigned address, unsigned cc) { return RDMEM_impl(address, cc); } template template NEVER_INLINE unsigned CPUCore::RD_WORD_slow(unsigned address, unsigned cc) { unsigned res = RDMEM_impl(address, cc); res += RDMEM_impl((address + 1) & 0xFFFF, cc + T::CC_RDMEM) << 8; return res; } template template ALWAYS_INLINE unsigned CPUCore::RD_WORD_impl2(unsigned address, unsigned cc) { const byte* line = readCacheLine[address >> CacheLine::BITS]; if (likely(((address & CacheLine::LOW) != CacheLine::LOW) && line)) { // fast path: cached and two bytes in same cache line T::template PRE_WORD(address); T::template POST_WORD< POST_PB>(address); return Endian::read_UA_L16(&line[address]); } else { // slow path, not inline return RD_WORD_slow(address, cc); } } template template ALWAYS_INLINE unsigned CPUCore::RD_WORD_impl(unsigned address, unsigned cc) { static const bool PRE = T::template Normalize::value; static const bool POST = T::template Normalize::value; return RD_WORD_impl2(address, cc); } template ALWAYS_INLINE unsigned CPUCore::RD_WORD_PC(unsigned cc) { unsigned addr = getPC(); setPC(addr + 2); return RD_WORD_impl(addr, cc); } template ALWAYS_INLINE unsigned CPUCore::RD_WORD( unsigned address, unsigned cc) { return RD_WORD_impl(address, cc); } template template NEVER_INLINE void CPUCore::WRMEMslow(unsigned address, byte value, unsigned cc) { // not cached unsigned high = address >> CacheLine::BITS; if (!writeCacheTried[high]) { // try to cache now unsigned addrBase = address & CacheLine::HIGH; if (byte* line = interface->getWriteCacheLine(addrBase)) { // cached ok T::template PRE_MEM(address); T::template POST_MEM< POST_PB>(address); writeCacheLine[high] = line - addrBase; writeCacheLine[high][address] = value; return; } } // uncacheable writeCacheTried[high] = true; T::template PRE_MEM(address); EmuTime time = T::getTimeFast(cc); scheduler.schedule(time); interface->writeMem(address, value, time); T::template POST_MEM(address); } template template ALWAYS_INLINE void CPUCore::WRMEM_impl2( unsigned address, byte value, unsigned cc) { byte* line = writeCacheLine[address >> CacheLine::BITS]; if (likely(line != nullptr)) { // cached, fast path T::template PRE_MEM(address); T::template POST_MEM< POST_PB>(address); line[address] = value; } else { WRMEMslow(address, value, cc); // not inlined } } template template ALWAYS_INLINE void CPUCore::WRMEM_impl( unsigned address, byte value, unsigned cc) { static const bool PRE = T::template Normalize::value; static const bool POST = T::template Normalize::value; WRMEM_impl2(address, value, cc); } template ALWAYS_INLINE void CPUCore::WRMEM( unsigned address, byte value, unsigned cc) { WRMEM_impl(address, value, cc); } template NEVER_INLINE void CPUCore::WR_WORD_slow( unsigned address, unsigned value, unsigned cc) { WRMEM_impl( address, value & 255, cc); WRMEM_impl((address + 1) & 0xFFFF, value >> 8, cc + T::CC_WRMEM); } template ALWAYS_INLINE void CPUCore::WR_WORD( unsigned address, unsigned value, unsigned cc) { byte* line = writeCacheLine[address >> CacheLine::BITS]; if (likely(((address & CacheLine::LOW) != CacheLine::LOW) && line)) { // fast path: cached and two bytes in same cache line T::template PRE_WORD(address); T::template POST_WORD< true>(address); Endian::write_UA_L16(&line[address], value); } else { // slow path, not inline WR_WORD_slow(address, value, cc); } } // same as WR_WORD, but writes high byte first template template NEVER_INLINE void CPUCore::WR_WORD_rev_slow( unsigned address, unsigned value, unsigned cc) { WRMEM_impl((address + 1) & 0xFFFF, value >> 8, cc); WRMEM_impl( address, value & 255, cc + T::CC_WRMEM); } template template ALWAYS_INLINE void CPUCore::WR_WORD_rev2( unsigned address, unsigned value, unsigned cc) { byte* line = writeCacheLine[address >> CacheLine::BITS]; if (likely(((address & CacheLine::LOW) != CacheLine::LOW) && line)) { // fast path: cached and two bytes in same cache line T::template PRE_WORD(address); T::template POST_WORD< POST_PB>(address); Endian::write_UA_L16(&line[address], value); } else { // slow path, not inline WR_WORD_rev_slow(address, value, cc); } } template template ALWAYS_INLINE void CPUCore::WR_WORD_rev( unsigned address, unsigned value, unsigned cc) { static const bool PRE = T::template Normalize::value; static const bool POST = T::template Normalize::value; WR_WORD_rev2(address, value, cc); } // NMI interrupt template inline void CPUCore::nmi() { incR(1); setHALT(false); setIFF1(false); PUSH(getPC()); setPC(0x0066); T::add(T::CC_NMI); } // IM0 interrupt template inline void CPUCore::irq0() { // TODO current implementation only works for 1-byte instructions // ok for MSX assert(interface->readIRQVector() == 0xFF); incR(1); setHALT(false); setIFF1(false); setIFF2(false); PUSH(getPC()); setPC(0x0038); T::setMemPtr(getPC()); T::add(T::CC_IRQ0); } // IM1 interrupt template inline void CPUCore::irq1() { incR(1); setHALT(false); setIFF1(false); setIFF2(false); PUSH(getPC()); setPC(0x0038); T::setMemPtr(getPC()); T::add(T::CC_IRQ1); } // IM2 interrupt template inline void CPUCore::irq2() { incR(1); setHALT(false); setIFF1(false); setIFF2(false); PUSH(getPC()); unsigned x = interface->readIRQVector() | (getI() << 8); setPC(RD_WORD(x, T::CC_IRQ2_2)); T::setMemPtr(getPC()); T::add(T::CC_IRQ2); } template void CPUCore::executeInstructions() { assert(isNextAfterClear()); #ifdef USE_COMPUTED_GOTO // Addresses of all main-opcode routines, // Note that 40/49/53/5B/64/6D/7F is replaced by 00 (ld r,r == nop) static void* opcodeTable[256] = { &&op00, &&op01, &&op02, &&op03, &&op04, &&op05, &&op06, &&op07, &&op08, &&op09, &&op0A, &&op0B, &&op0C, &&op0D, &&op0E, &&op0F, &&op10, &&op11, &&op12, &&op13, &&op14, &&op15, &&op16, &&op17, &&op18, &&op19, &&op1A, &&op1B, &&op1C, &&op1D, &&op1E, &&op1F, &&op20, &&op21, &&op22, &&op23, &&op24, &&op25, &&op26, &&op27, &&op28, &&op29, &&op2A, &&op2B, &&op2C, &&op2D, &&op2E, &&op2F, &&op30, &&op31, &&op32, &&op33, &&op34, &&op35, &&op36, &&op37, &&op38, &&op39, &&op3A, &&op3B, &&op3C, &&op3D, &&op3E, &&op3F, &&op00, &&op41, &&op42, &&op43, &&op44, &&op45, &&op46, &&op47, &&op48, &&op00, &&op4A, &&op4B, &&op4C, &&op4D, &&op4E, &&op4F, &&op50, &&op51, &&op00, &&op53, &&op54, &&op55, &&op56, &&op57, &&op58, &&op59, &&op5A, &&op00, &&op5C, &&op5D, &&op5E, &&op5F, &&op60, &&op61, &&op62, &&op63, &&op00, &&op65, &&op66, &&op67, &&op68, &&op69, &&op6A, &&op6B, &&op6C, &&op00, &&op6E, &&op6F, &&op70, &&op71, &&op72, &&op73, &&op74, &&op75, &&op76, &&op77, &&op78, &&op79, &&op7A, &&op7B, &&op7C, &&op7D, &&op7E, &&op00, &&op80, &&op81, &&op82, &&op83, &&op84, &&op85, &&op86, &&op87, &&op88, &&op89, &&op8A, &&op8B, &&op8C, &&op8D, &&op8E, &&op8F, &&op90, &&op91, &&op92, &&op93, &&op94, &&op95, &&op96, &&op97, &&op98, &&op99, &&op9A, &&op9B, &&op9C, &&op9D, &&op9E, &&op9F, &&opA0, &&opA1, &&opA2, &&opA3, &&opA4, &&opA5, &&opA6, &&opA7, &&opA8, &&opA9, &&opAA, &&opAB, &&opAC, &&opAD, &&opAE, &&opAF, &&opB0, &&opB1, &&opB2, &&opB3, &&opB4, &&opB5, &&opB6, &&opB7, &&opB8, &&opB9, &&opBA, &&opBB, &&opBC, &&opBD, &&opBE, &&opBF, &&opC0, &&opC1, &&opC2, &&opC3, &&opC4, &&opC5, &&opC6, &&opC7, &&opC8, &&opC9, &&opCA, &&opCB, &&opCC, &&opCD, &&opCE, &&opCF, &&opD0, &&opD1, &&opD2, &&opD3, &&opD4, &&opD5, &&opD6, &&opD7, &&opD8, &&opD9, &&opDA, &&opDB, &&opDC, &&opDD, &&opDE, &&opDF, &&opE0, &&opE1, &&opE2, &&opE3, &&opE4, &&opE5, &&opE6, &&opE7, &&opE8, &&opE9, &&opEA, &&opEB, &&opEC, &&opED, &&opEE, &&opEF, &&opF0, &&opF1, &&opF2, &&opF3, &&opF4, &&opF5, &&opF6, &&opF7, &&opF8, &&opF9, &&opFA, &&opFB, &&opFC, &&opFD, &&opFE, &&opFF, }; // Check T::limitReached(). If it's OK to continue, // fetch and execute next instruction. #define NEXT \ T::add(c); \ T::R800Refresh(*this); \ if (likely(!T::limitReached())) { \ incR(1); \ unsigned address = getPC(); \ const byte* line = readCacheLine[address >> CacheLine::BITS]; \ if (likely(line != nullptr)) { \ setPC(address + 1); \ T::template PRE_MEM(address); \ T::template POST_MEM< false>(address); \ byte op = line[address]; \ goto *(opcodeTable[op]); \ } else { \ goto fetchSlow; \ } \ } \ return; // After some instructions we must always exit the CPU loop (ei, halt, retn) #define NEXT_STOP \ T::add(c); \ T::R800Refresh(*this); \ assert(T::limitReached()); \ return; #define NEXT_EI \ T::add(c); \ /* !! NO T::R800Refresh(*this); !! */ \ assert(T::limitReached()); \ return; // Define a label (instead of case in a switch statement) #define CASE(X) op##X: #else // USE_COMPUTED_GOTO #define NEXT \ T::add(c); \ T::R800Refresh(*this); \ if (likely(!T::limitReached())) { \ goto start; \ } \ return; #define NEXT_STOP \ T::add(c); \ T::R800Refresh(*this); \ assert(T::limitReached()); \ return; #define NEXT_EI \ T::add(c); \ /* !! NO T::R800Refresh(*this); !! */ \ assert(T::limitReached()); \ return; #define CASE(X) case 0x##X: #endif // USE_COMPUTED_GOTO #ifndef USE_COMPUTED_GOTO start: #endif unsigned ixy; // for dd_cb/fd_cb byte opcodeMain = RDMEM_OPCODE(T::CC_MAIN); incR(1); #ifdef USE_COMPUTED_GOTO goto *(opcodeTable[opcodeMain]); fetchSlow: { unsigned address = getPC(); setPC(address + 1); byte opcodeSlow = RDMEMslow(address, T::CC_MAIN); goto *(opcodeTable[opcodeSlow]); } #endif #ifndef USE_COMPUTED_GOTO switchopcode: switch (opcodeMain) { CASE(40) // ld b,b CASE(49) // ld c,c CASE(52) // ld d,d CASE(5B) // ld e,e CASE(64) // ld h,h CASE(6D) // ld l,l CASE(7F) // ld a,a #endif CASE(00) { int c = nop(); NEXT; } CASE(07) { int c = rlca(); NEXT; } CASE(0F) { int c = rrca(); NEXT; } CASE(17) { int c = rla(); NEXT; } CASE(1F) { int c = rra(); NEXT; } CASE(08) { int c = ex_af_af(); NEXT; } CASE(27) { int c = daa(); NEXT; } CASE(2F) { int c = cpl(); NEXT; } CASE(37) { int c = scf(); NEXT; } CASE(3F) { int c = ccf(); NEXT; } CASE(20) { int c = jr(CondNZ()); NEXT; } CASE(28) { int c = jr(CondZ ()); NEXT; } CASE(30) { int c = jr(CondNC()); NEXT; } CASE(38) { int c = jr(CondC ()); NEXT; } CASE(18) { int c = jr(CondTrue()); NEXT; } CASE(10) { int c = djnz(); NEXT; } CASE(32) { int c = ld_xbyte_a(); NEXT; } CASE(3A) { int c = ld_a_xbyte(); NEXT; } CASE(22) { int c = ld_xword_SS(); NEXT; } CASE(2A) { int c = ld_SS_xword(); NEXT; } CASE(02) { int c = ld_SS_a(); NEXT; } CASE(12) { int c = ld_SS_a(); NEXT; } CASE(1A) { int c = ld_a_SS(); NEXT; } CASE(0A) { int c = ld_a_SS(); NEXT; } CASE(03) { int c = inc_SS(); NEXT; } CASE(13) { int c = inc_SS(); NEXT; } CASE(23) { int c = inc_SS(); NEXT; } CASE(33) { int c = inc_SS(); NEXT; } CASE(0B) { int c = dec_SS(); NEXT; } CASE(1B) { int c = dec_SS(); NEXT; } CASE(2B) { int c = dec_SS(); NEXT; } CASE(3B) { int c = dec_SS(); NEXT; } CASE(09) { int c = add_SS_TT(); NEXT; } CASE(19) { int c = add_SS_TT(); NEXT; } CASE(29) { int c = add_SS_SS(); NEXT; } CASE(39) { int c = add_SS_TT(); NEXT; } CASE(01) { int c = ld_SS_word(); NEXT; } CASE(11) { int c = ld_SS_word(); NEXT; } CASE(21) { int c = ld_SS_word(); NEXT; } CASE(31) { int c = ld_SS_word(); NEXT; } CASE(04) { int c = inc_R(); NEXT; } CASE(0C) { int c = inc_R(); NEXT; } CASE(14) { int c = inc_R(); NEXT; } CASE(1C) { int c = inc_R(); NEXT; } CASE(24) { int c = inc_R(); NEXT; } CASE(2C) { int c = inc_R(); NEXT; } CASE(3C) { int c = inc_R(); NEXT; } CASE(34) { int c = inc_xhl(); NEXT; } CASE(05) { int c = dec_R(); NEXT; } CASE(0D) { int c = dec_R(); NEXT; } CASE(15) { int c = dec_R(); NEXT; } CASE(1D) { int c = dec_R(); NEXT; } CASE(25) { int c = dec_R(); NEXT; } CASE(2D) { int c = dec_R(); NEXT; } CASE(3D) { int c = dec_R(); NEXT; } CASE(35) { int c = dec_xhl(); NEXT; } CASE(06) { int c = ld_R_byte(); NEXT; } CASE(0E) { int c = ld_R_byte(); NEXT; } CASE(16) { int c = ld_R_byte(); NEXT; } CASE(1E) { int c = ld_R_byte(); NEXT; } CASE(26) { int c = ld_R_byte(); NEXT; } CASE(2E) { int c = ld_R_byte(); NEXT; } CASE(3E) { int c = ld_R_byte(); NEXT; } CASE(36) { int c = ld_xhl_byte(); NEXT; } CASE(41) { int c = ld_R_R(); NEXT; } CASE(42) { int c = ld_R_R(); NEXT; } CASE(43) { int c = ld_R_R(); NEXT; } CASE(44) { int c = ld_R_R(); NEXT; } CASE(45) { int c = ld_R_R(); NEXT; } CASE(47) { int c = ld_R_R(); NEXT; } CASE(48) { int c = ld_R_R(); NEXT; } CASE(4A) { int c = ld_R_R(); NEXT; } CASE(4B) { int c = ld_R_R(); NEXT; } CASE(4C) { int c = ld_R_R(); NEXT; } CASE(4D) { int c = ld_R_R(); NEXT; } CASE(4F) { int c = ld_R_R(); NEXT; } CASE(50) { int c = ld_R_R(); NEXT; } CASE(51) { int c = ld_R_R(); NEXT; } CASE(53) { int c = ld_R_R(); NEXT; } CASE(54) { int c = ld_R_R(); NEXT; } CASE(55) { int c = ld_R_R(); NEXT; } CASE(57) { int c = ld_R_R(); NEXT; } CASE(58) { int c = ld_R_R(); NEXT; } CASE(59) { int c = ld_R_R(); NEXT; } CASE(5A) { int c = ld_R_R(); NEXT; } CASE(5C) { int c = ld_R_R(); NEXT; } CASE(5D) { int c = ld_R_R(); NEXT; } CASE(5F) { int c = ld_R_R(); NEXT; } CASE(60) { int c = ld_R_R(); NEXT; } CASE(61) { int c = ld_R_R(); NEXT; } CASE(62) { int c = ld_R_R(); NEXT; } CASE(63) { int c = ld_R_R(); NEXT; } CASE(65) { int c = ld_R_R(); NEXT; } CASE(67) { int c = ld_R_R(); NEXT; } CASE(68) { int c = ld_R_R(); NEXT; } CASE(69) { int c = ld_R_R(); NEXT; } CASE(6A) { int c = ld_R_R(); NEXT; } CASE(6B) { int c = ld_R_R(); NEXT; } CASE(6C) { int c = ld_R_R(); NEXT; } CASE(6F) { int c = ld_R_R(); NEXT; } CASE(78) { int c = ld_R_R(); NEXT; } CASE(79) { int c = ld_R_R(); NEXT; } CASE(7A) { int c = ld_R_R(); NEXT; } CASE(7B) { int c = ld_R_R(); NEXT; } CASE(7C) { int c = ld_R_R(); NEXT; } CASE(7D) { int c = ld_R_R(); NEXT; } CASE(70) { int c = ld_xhl_R(); NEXT; } CASE(71) { int c = ld_xhl_R(); NEXT; } CASE(72) { int c = ld_xhl_R(); NEXT; } CASE(73) { int c = ld_xhl_R(); NEXT; } CASE(74) { int c = ld_xhl_R(); NEXT; } CASE(75) { int c = ld_xhl_R(); NEXT; } CASE(77) { int c = ld_xhl_R(); NEXT; } CASE(46) { int c = ld_R_xhl(); NEXT; } CASE(4E) { int c = ld_R_xhl(); NEXT; } CASE(56) { int c = ld_R_xhl(); NEXT; } CASE(5E) { int c = ld_R_xhl(); NEXT; } CASE(66) { int c = ld_R_xhl(); NEXT; } CASE(6E) { int c = ld_R_xhl(); NEXT; } CASE(7E) { int c = ld_R_xhl(); NEXT; } CASE(76) { int c = halt(); NEXT_STOP; } CASE(80) { int c = add_a_R(); NEXT; } CASE(81) { int c = add_a_R(); NEXT; } CASE(82) { int c = add_a_R(); NEXT; } CASE(83) { int c = add_a_R(); NEXT; } CASE(84) { int c = add_a_R(); NEXT; } CASE(85) { int c = add_a_R(); NEXT; } CASE(86) { int c = add_a_xhl(); NEXT; } CASE(87) { int c = add_a_a(); NEXT; } CASE(88) { int c = adc_a_R(); NEXT; } CASE(89) { int c = adc_a_R(); NEXT; } CASE(8A) { int c = adc_a_R(); NEXT; } CASE(8B) { int c = adc_a_R(); NEXT; } CASE(8C) { int c = adc_a_R(); NEXT; } CASE(8D) { int c = adc_a_R(); NEXT; } CASE(8E) { int c = adc_a_xhl(); NEXT; } CASE(8F) { int c = adc_a_a(); NEXT; } CASE(90) { int c = sub_R(); NEXT; } CASE(91) { int c = sub_R(); NEXT; } CASE(92) { int c = sub_R(); NEXT; } CASE(93) { int c = sub_R(); NEXT; } CASE(94) { int c = sub_R(); NEXT; } CASE(95) { int c = sub_R(); NEXT; } CASE(96) { int c = sub_xhl(); NEXT; } CASE(97) { int c = sub_a(); NEXT; } CASE(98) { int c = sbc_a_R(); NEXT; } CASE(99) { int c = sbc_a_R(); NEXT; } CASE(9A) { int c = sbc_a_R(); NEXT; } CASE(9B) { int c = sbc_a_R(); NEXT; } CASE(9C) { int c = sbc_a_R(); NEXT; } CASE(9D) { int c = sbc_a_R(); NEXT; } CASE(9E) { int c = sbc_a_xhl(); NEXT; } CASE(9F) { int c = sbc_a_a(); NEXT; } CASE(A0) { int c = and_R(); NEXT; } CASE(A1) { int c = and_R(); NEXT; } CASE(A2) { int c = and_R(); NEXT; } CASE(A3) { int c = and_R(); NEXT; } CASE(A4) { int c = and_R(); NEXT; } CASE(A5) { int c = and_R(); NEXT; } CASE(A6) { int c = and_xhl(); NEXT; } CASE(A7) { int c = and_a(); NEXT; } CASE(A8) { int c = xor_R(); NEXT; } CASE(A9) { int c = xor_R(); NEXT; } CASE(AA) { int c = xor_R(); NEXT; } CASE(AB) { int c = xor_R(); NEXT; } CASE(AC) { int c = xor_R(); NEXT; } CASE(AD) { int c = xor_R(); NEXT; } CASE(AE) { int c = xor_xhl(); NEXT; } CASE(AF) { int c = xor_a(); NEXT; } CASE(B0) { int c = or_R(); NEXT; } CASE(B1) { int c = or_R(); NEXT; } CASE(B2) { int c = or_R(); NEXT; } CASE(B3) { int c = or_R(); NEXT; } CASE(B4) { int c = or_R(); NEXT; } CASE(B5) { int c = or_R(); NEXT; } CASE(B6) { int c = or_xhl(); NEXT; } CASE(B7) { int c = or_a(); NEXT; } CASE(B8) { int c = cp_R(); NEXT; } CASE(B9) { int c = cp_R(); NEXT; } CASE(BA) { int c = cp_R(); NEXT; } CASE(BB) { int c = cp_R(); NEXT; } CASE(BC) { int c = cp_R(); NEXT; } CASE(BD) { int c = cp_R(); NEXT; } CASE(BE) { int c = cp_xhl(); NEXT; } CASE(BF) { int c = cp_a(); NEXT; } CASE(D3) { int c = out_byte_a(); NEXT; } CASE(DB) { int c = in_a_byte(); NEXT; } CASE(D9) { int c = exx(); NEXT; } CASE(E3) { int c = ex_xsp_SS(); NEXT; } CASE(EB) { int c = ex_de_hl(); NEXT; } CASE(E9) { int c = jp_SS(); NEXT; } CASE(F9) { int c = ld_sp_SS(); NEXT; } CASE(F3) { int c = di(); NEXT; } CASE(FB) { int c = ei(); NEXT_EI; } CASE(C6) { int c = add_a_byte(); NEXT; } CASE(CE) { int c = adc_a_byte(); NEXT; } CASE(D6) { int c = sub_byte(); NEXT; } CASE(DE) { int c = sbc_a_byte(); NEXT; } CASE(E6) { int c = and_byte(); NEXT; } CASE(EE) { int c = xor_byte(); NEXT; } CASE(F6) { int c = or_byte(); NEXT; } CASE(FE) { int c = cp_byte(); NEXT; } CASE(C0) { int c = ret(CondNZ()); NEXT; } CASE(C8) { int c = ret(CondZ ()); NEXT; } CASE(D0) { int c = ret(CondNC()); NEXT; } CASE(D8) { int c = ret(CondC ()); NEXT; } CASE(E0) { int c = ret(CondPO()); NEXT; } CASE(E8) { int c = ret(CondPE()); NEXT; } CASE(F0) { int c = ret(CondP ()); NEXT; } CASE(F8) { int c = ret(CondM ()); NEXT; } CASE(C9) { int c = ret(); NEXT; } CASE(C2) { int c = jp(CondNZ()); NEXT; } CASE(CA) { int c = jp(CondZ ()); NEXT; } CASE(D2) { int c = jp(CondNC()); NEXT; } CASE(DA) { int c = jp(CondC ()); NEXT; } CASE(E2) { int c = jp(CondPO()); NEXT; } CASE(EA) { int c = jp(CondPE()); NEXT; } CASE(F2) { int c = jp(CondP ()); NEXT; } CASE(FA) { int c = jp(CondM ()); NEXT; } CASE(C3) { int c = jp(CondTrue()); NEXT; } CASE(C4) { int c = call(CondNZ()); NEXT; } CASE(CC) { int c = call(CondZ ()); NEXT; } CASE(D4) { int c = call(CondNC()); NEXT; } CASE(DC) { int c = call(CondC ()); NEXT; } CASE(E4) { int c = call(CondPO()); NEXT; } CASE(EC) { int c = call(CondPE()); NEXT; } CASE(F4) { int c = call(CondP ()); NEXT; } CASE(FC) { int c = call(CondM ()); NEXT; } CASE(CD) { int c = call(CondTrue()); NEXT; } CASE(C1) { int c = pop_SS (); NEXT; } CASE(D1) { int c = pop_SS (); NEXT; } CASE(E1) { int c = pop_SS (); NEXT; } CASE(F1) { int c = pop_SS (); NEXT; } CASE(C5) { int c = push_SS(); NEXT; } CASE(D5) { int c = push_SS(); NEXT; } CASE(E5) { int c = push_SS(); NEXT; } CASE(F5) { int c = push_SS(); NEXT; } CASE(C7) { int c = rst<0x00>(); NEXT; } CASE(CF) { int c = rst<0x08>(); NEXT; } CASE(D7) { int c = rst<0x10>(); NEXT; } CASE(DF) { int c = rst<0x18>(); NEXT; } CASE(E7) { int c = rst<0x20>(); NEXT; } CASE(EF) { int c = rst<0x28>(); NEXT; } CASE(F7) { int c = rst<0x30>(); NEXT; } CASE(FF) { int c = rst<0x38>(); NEXT; } CASE(CB) { byte cb_opcode = RDMEM_OPCODE(T::CC_PREFIX); incR(1); switch (cb_opcode) { case 0x00: { int c = rlc_R(); NEXT; } case 0x01: { int c = rlc_R(); NEXT; } case 0x02: { int c = rlc_R(); NEXT; } case 0x03: { int c = rlc_R(); NEXT; } case 0x04: { int c = rlc_R(); NEXT; } case 0x05: { int c = rlc_R(); NEXT; } case 0x07: { int c = rlc_R(); NEXT; } case 0x06: { int c = rlc_xhl(); NEXT; } case 0x08: { int c = rrc_R(); NEXT; } case 0x09: { int c = rrc_R(); NEXT; } case 0x0a: { int c = rrc_R(); NEXT; } case 0x0b: { int c = rrc_R(); NEXT; } case 0x0c: { int c = rrc_R(); NEXT; } case 0x0d: { int c = rrc_R(); NEXT; } case 0x0f: { int c = rrc_R(); NEXT; } case 0x0e: { int c = rrc_xhl(); NEXT; } case 0x10: { int c = rl_R(); NEXT; } case 0x11: { int c = rl_R(); NEXT; } case 0x12: { int c = rl_R(); NEXT; } case 0x13: { int c = rl_R(); NEXT; } case 0x14: { int c = rl_R(); NEXT; } case 0x15: { int c = rl_R(); NEXT; } case 0x17: { int c = rl_R(); NEXT; } case 0x16: { int c = rl_xhl(); NEXT; } case 0x18: { int c = rr_R(); NEXT; } case 0x19: { int c = rr_R(); NEXT; } case 0x1a: { int c = rr_R(); NEXT; } case 0x1b: { int c = rr_R(); NEXT; } case 0x1c: { int c = rr_R(); NEXT; } case 0x1d: { int c = rr_R(); NEXT; } case 0x1f: { int c = rr_R(); NEXT; } case 0x1e: { int c = rr_xhl(); NEXT; } case 0x20: { int c = sla_R(); NEXT; } case 0x21: { int c = sla_R(); NEXT; } case 0x22: { int c = sla_R(); NEXT; } case 0x23: { int c = sla_R(); NEXT; } case 0x24: { int c = sla_R(); NEXT; } case 0x25: { int c = sla_R(); NEXT; } case 0x27: { int c = sla_R(); NEXT; } case 0x26: { int c = sla_xhl(); NEXT; } case 0x28: { int c = sra_R(); NEXT; } case 0x29: { int c = sra_R(); NEXT; } case 0x2a: { int c = sra_R(); NEXT; } case 0x2b: { int c = sra_R(); NEXT; } case 0x2c: { int c = sra_R(); NEXT; } case 0x2d: { int c = sra_R(); NEXT; } case 0x2f: { int c = sra_R(); NEXT; } case 0x2e: { int c = sra_xhl(); NEXT; } case 0x30: { int c = T::isR800() ? sla_R() : sll_R(); NEXT; } case 0x31: { int c = T::isR800() ? sla_R() : sll_R(); NEXT; } case 0x32: { int c = T::isR800() ? sla_R() : sll_R(); NEXT; } case 0x33: { int c = T::isR800() ? sla_R() : sll_R(); NEXT; } case 0x34: { int c = T::isR800() ? sla_R() : sll_R(); NEXT; } case 0x35: { int c = T::isR800() ? sla_R() : sll_R(); NEXT; } case 0x37: { int c = T::isR800() ? sla_R() : sll_R(); NEXT; } case 0x36: { int c = T::isR800() ? sla_xhl() : sll_xhl(); NEXT; } case 0x38: { int c = srl_R(); NEXT; } case 0x39: { int c = srl_R(); NEXT; } case 0x3a: { int c = srl_R(); NEXT; } case 0x3b: { int c = srl_R(); NEXT; } case 0x3c: { int c = srl_R(); NEXT; } case 0x3d: { int c = srl_R(); NEXT; } case 0x3f: { int c = srl_R(); NEXT; } case 0x3e: { int c = srl_xhl(); NEXT; } case 0x40: { int c = bit_N_R<0,B>(); NEXT; } case 0x41: { int c = bit_N_R<0,C>(); NEXT; } case 0x42: { int c = bit_N_R<0,D>(); NEXT; } case 0x43: { int c = bit_N_R<0,E>(); NEXT; } case 0x44: { int c = bit_N_R<0,H>(); NEXT; } case 0x45: { int c = bit_N_R<0,L>(); NEXT; } case 0x47: { int c = bit_N_R<0,A>(); NEXT; } case 0x48: { int c = bit_N_R<1,B>(); NEXT; } case 0x49: { int c = bit_N_R<1,C>(); NEXT; } case 0x4a: { int c = bit_N_R<1,D>(); NEXT; } case 0x4b: { int c = bit_N_R<1,E>(); NEXT; } case 0x4c: { int c = bit_N_R<1,H>(); NEXT; } case 0x4d: { int c = bit_N_R<1,L>(); NEXT; } case 0x4f: { int c = bit_N_R<1,A>(); NEXT; } case 0x50: { int c = bit_N_R<2,B>(); NEXT; } case 0x51: { int c = bit_N_R<2,C>(); NEXT; } case 0x52: { int c = bit_N_R<2,D>(); NEXT; } case 0x53: { int c = bit_N_R<2,E>(); NEXT; } case 0x54: { int c = bit_N_R<2,H>(); NEXT; } case 0x55: { int c = bit_N_R<2,L>(); NEXT; } case 0x57: { int c = bit_N_R<2,A>(); NEXT; } case 0x58: { int c = bit_N_R<3,B>(); NEXT; } case 0x59: { int c = bit_N_R<3,C>(); NEXT; } case 0x5a: { int c = bit_N_R<3,D>(); NEXT; } case 0x5b: { int c = bit_N_R<3,E>(); NEXT; } case 0x5c: { int c = bit_N_R<3,H>(); NEXT; } case 0x5d: { int c = bit_N_R<3,L>(); NEXT; } case 0x5f: { int c = bit_N_R<3,A>(); NEXT; } case 0x60: { int c = bit_N_R<4,B>(); NEXT; } case 0x61: { int c = bit_N_R<4,C>(); NEXT; } case 0x62: { int c = bit_N_R<4,D>(); NEXT; } case 0x63: { int c = bit_N_R<4,E>(); NEXT; } case 0x64: { int c = bit_N_R<4,H>(); NEXT; } case 0x65: { int c = bit_N_R<4,L>(); NEXT; } case 0x67: { int c = bit_N_R<4,A>(); NEXT; } case 0x68: { int c = bit_N_R<5,B>(); NEXT; } case 0x69: { int c = bit_N_R<5,C>(); NEXT; } case 0x6a: { int c = bit_N_R<5,D>(); NEXT; } case 0x6b: { int c = bit_N_R<5,E>(); NEXT; } case 0x6c: { int c = bit_N_R<5,H>(); NEXT; } case 0x6d: { int c = bit_N_R<5,L>(); NEXT; } case 0x6f: { int c = bit_N_R<5,A>(); NEXT; } case 0x70: { int c = bit_N_R<6,B>(); NEXT; } case 0x71: { int c = bit_N_R<6,C>(); NEXT; } case 0x72: { int c = bit_N_R<6,D>(); NEXT; } case 0x73: { int c = bit_N_R<6,E>(); NEXT; } case 0x74: { int c = bit_N_R<6,H>(); NEXT; } case 0x75: { int c = bit_N_R<6,L>(); NEXT; } case 0x77: { int c = bit_N_R<6,A>(); NEXT; } case 0x78: { int c = bit_N_R<7,B>(); NEXT; } case 0x79: { int c = bit_N_R<7,C>(); NEXT; } case 0x7a: { int c = bit_N_R<7,D>(); NEXT; } case 0x7b: { int c = bit_N_R<7,E>(); NEXT; } case 0x7c: { int c = bit_N_R<7,H>(); NEXT; } case 0x7d: { int c = bit_N_R<7,L>(); NEXT; } case 0x7f: { int c = bit_N_R<7,A>(); NEXT; } case 0x46: { int c = bit_N_xhl<0>(); NEXT; } case 0x4e: { int c = bit_N_xhl<1>(); NEXT; } case 0x56: { int c = bit_N_xhl<2>(); NEXT; } case 0x5e: { int c = bit_N_xhl<3>(); NEXT; } case 0x66: { int c = bit_N_xhl<4>(); NEXT; } case 0x6e: { int c = bit_N_xhl<5>(); NEXT; } case 0x76: { int c = bit_N_xhl<6>(); NEXT; } case 0x7e: { int c = bit_N_xhl<7>(); NEXT; } case 0x80: { int c = res_N_R<0,B>(); NEXT; } case 0x81: { int c = res_N_R<0,C>(); NEXT; } case 0x82: { int c = res_N_R<0,D>(); NEXT; } case 0x83: { int c = res_N_R<0,E>(); NEXT; } case 0x84: { int c = res_N_R<0,H>(); NEXT; } case 0x85: { int c = res_N_R<0,L>(); NEXT; } case 0x87: { int c = res_N_R<0,A>(); NEXT; } case 0x88: { int c = res_N_R<1,B>(); NEXT; } case 0x89: { int c = res_N_R<1,C>(); NEXT; } case 0x8a: { int c = res_N_R<1,D>(); NEXT; } case 0x8b: { int c = res_N_R<1,E>(); NEXT; } case 0x8c: { int c = res_N_R<1,H>(); NEXT; } case 0x8d: { int c = res_N_R<1,L>(); NEXT; } case 0x8f: { int c = res_N_R<1,A>(); NEXT; } case 0x90: { int c = res_N_R<2,B>(); NEXT; } case 0x91: { int c = res_N_R<2,C>(); NEXT; } case 0x92: { int c = res_N_R<2,D>(); NEXT; } case 0x93: { int c = res_N_R<2,E>(); NEXT; } case 0x94: { int c = res_N_R<2,H>(); NEXT; } case 0x95: { int c = res_N_R<2,L>(); NEXT; } case 0x97: { int c = res_N_R<2,A>(); NEXT; } case 0x98: { int c = res_N_R<3,B>(); NEXT; } case 0x99: { int c = res_N_R<3,C>(); NEXT; } case 0x9a: { int c = res_N_R<3,D>(); NEXT; } case 0x9b: { int c = res_N_R<3,E>(); NEXT; } case 0x9c: { int c = res_N_R<3,H>(); NEXT; } case 0x9d: { int c = res_N_R<3,L>(); NEXT; } case 0x9f: { int c = res_N_R<3,A>(); NEXT; } case 0xa0: { int c = res_N_R<4,B>(); NEXT; } case 0xa1: { int c = res_N_R<4,C>(); NEXT; } case 0xa2: { int c = res_N_R<4,D>(); NEXT; } case 0xa3: { int c = res_N_R<4,E>(); NEXT; } case 0xa4: { int c = res_N_R<4,H>(); NEXT; } case 0xa5: { int c = res_N_R<4,L>(); NEXT; } case 0xa7: { int c = res_N_R<4,A>(); NEXT; } case 0xa8: { int c = res_N_R<5,B>(); NEXT; } case 0xa9: { int c = res_N_R<5,C>(); NEXT; } case 0xaa: { int c = res_N_R<5,D>(); NEXT; } case 0xab: { int c = res_N_R<5,E>(); NEXT; } case 0xac: { int c = res_N_R<5,H>(); NEXT; } case 0xad: { int c = res_N_R<5,L>(); NEXT; } case 0xaf: { int c = res_N_R<5,A>(); NEXT; } case 0xb0: { int c = res_N_R<6,B>(); NEXT; } case 0xb1: { int c = res_N_R<6,C>(); NEXT; } case 0xb2: { int c = res_N_R<6,D>(); NEXT; } case 0xb3: { int c = res_N_R<6,E>(); NEXT; } case 0xb4: { int c = res_N_R<6,H>(); NEXT; } case 0xb5: { int c = res_N_R<6,L>(); NEXT; } case 0xb7: { int c = res_N_R<6,A>(); NEXT; } case 0xb8: { int c = res_N_R<7,B>(); NEXT; } case 0xb9: { int c = res_N_R<7,C>(); NEXT; } case 0xba: { int c = res_N_R<7,D>(); NEXT; } case 0xbb: { int c = res_N_R<7,E>(); NEXT; } case 0xbc: { int c = res_N_R<7,H>(); NEXT; } case 0xbd: { int c = res_N_R<7,L>(); NEXT; } case 0xbf: { int c = res_N_R<7,A>(); NEXT; } case 0x86: { int c = res_N_xhl<0>(); NEXT; } case 0x8e: { int c = res_N_xhl<1>(); NEXT; } case 0x96: { int c = res_N_xhl<2>(); NEXT; } case 0x9e: { int c = res_N_xhl<3>(); NEXT; } case 0xa6: { int c = res_N_xhl<4>(); NEXT; } case 0xae: { int c = res_N_xhl<5>(); NEXT; } case 0xb6: { int c = res_N_xhl<6>(); NEXT; } case 0xbe: { int c = res_N_xhl<7>(); NEXT; } case 0xc0: { int c = set_N_R<0,B>(); NEXT; } case 0xc1: { int c = set_N_R<0,C>(); NEXT; } case 0xc2: { int c = set_N_R<0,D>(); NEXT; } case 0xc3: { int c = set_N_R<0,E>(); NEXT; } case 0xc4: { int c = set_N_R<0,H>(); NEXT; } case 0xc5: { int c = set_N_R<0,L>(); NEXT; } case 0xc7: { int c = set_N_R<0,A>(); NEXT; } case 0xc8: { int c = set_N_R<1,B>(); NEXT; } case 0xc9: { int c = set_N_R<1,C>(); NEXT; } case 0xca: { int c = set_N_R<1,D>(); NEXT; } case 0xcb: { int c = set_N_R<1,E>(); NEXT; } case 0xcc: { int c = set_N_R<1,H>(); NEXT; } case 0xcd: { int c = set_N_R<1,L>(); NEXT; } case 0xcf: { int c = set_N_R<1,A>(); NEXT; } case 0xd0: { int c = set_N_R<2,B>(); NEXT; } case 0xd1: { int c = set_N_R<2,C>(); NEXT; } case 0xd2: { int c = set_N_R<2,D>(); NEXT; } case 0xd3: { int c = set_N_R<2,E>(); NEXT; } case 0xd4: { int c = set_N_R<2,H>(); NEXT; } case 0xd5: { int c = set_N_R<2,L>(); NEXT; } case 0xd7: { int c = set_N_R<2,A>(); NEXT; } case 0xd8: { int c = set_N_R<3,B>(); NEXT; } case 0xd9: { int c = set_N_R<3,C>(); NEXT; } case 0xda: { int c = set_N_R<3,D>(); NEXT; } case 0xdb: { int c = set_N_R<3,E>(); NEXT; } case 0xdc: { int c = set_N_R<3,H>(); NEXT; } case 0xdd: { int c = set_N_R<3,L>(); NEXT; } case 0xdf: { int c = set_N_R<3,A>(); NEXT; } case 0xe0: { int c = set_N_R<4,B>(); NEXT; } case 0xe1: { int c = set_N_R<4,C>(); NEXT; } case 0xe2: { int c = set_N_R<4,D>(); NEXT; } case 0xe3: { int c = set_N_R<4,E>(); NEXT; } case 0xe4: { int c = set_N_R<4,H>(); NEXT; } case 0xe5: { int c = set_N_R<4,L>(); NEXT; } case 0xe7: { int c = set_N_R<4,A>(); NEXT; } case 0xe8: { int c = set_N_R<5,B>(); NEXT; } case 0xe9: { int c = set_N_R<5,C>(); NEXT; } case 0xea: { int c = set_N_R<5,D>(); NEXT; } case 0xeb: { int c = set_N_R<5,E>(); NEXT; } case 0xec: { int c = set_N_R<5,H>(); NEXT; } case 0xed: { int c = set_N_R<5,L>(); NEXT; } case 0xef: { int c = set_N_R<5,A>(); NEXT; } case 0xf0: { int c = set_N_R<6,B>(); NEXT; } case 0xf1: { int c = set_N_R<6,C>(); NEXT; } case 0xf2: { int c = set_N_R<6,D>(); NEXT; } case 0xf3: { int c = set_N_R<6,E>(); NEXT; } case 0xf4: { int c = set_N_R<6,H>(); NEXT; } case 0xf5: { int c = set_N_R<6,L>(); NEXT; } case 0xf7: { int c = set_N_R<6,A>(); NEXT; } case 0xf8: { int c = set_N_R<7,B>(); NEXT; } case 0xf9: { int c = set_N_R<7,C>(); NEXT; } case 0xfa: { int c = set_N_R<7,D>(); NEXT; } case 0xfb: { int c = set_N_R<7,E>(); NEXT; } case 0xfc: { int c = set_N_R<7,H>(); NEXT; } case 0xfd: { int c = set_N_R<7,L>(); NEXT; } case 0xff: { int c = set_N_R<7,A>(); NEXT; } case 0xc6: { int c = set_N_xhl<0>(); NEXT; } case 0xce: { int c = set_N_xhl<1>(); NEXT; } case 0xd6: { int c = set_N_xhl<2>(); NEXT; } case 0xde: { int c = set_N_xhl<3>(); NEXT; } case 0xe6: { int c = set_N_xhl<4>(); NEXT; } case 0xee: { int c = set_N_xhl<5>(); NEXT; } case 0xf6: { int c = set_N_xhl<6>(); NEXT; } case 0xfe: { int c = set_N_xhl<7>(); NEXT; } default: UNREACHABLE; return; } } CASE(ED) { byte ed_opcode = RDMEM_OPCODE(T::CC_PREFIX); incR(1); switch (ed_opcode) { case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f: case 0x77: case 0x7f: case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87: case 0x88: case 0x89: case 0x8a: case 0x8b: case 0x8c: case 0x8d: case 0x8e: case 0x8f: case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97: case 0x98: case 0x99: case 0x9a: case 0x9b: case 0x9c: case 0x9d: case 0x9e: case 0x9f: case 0xa4: case 0xa5: case 0xa6: case 0xa7: case 0xac: case 0xad: case 0xae: case 0xaf: case 0xb4: case 0xb5: case 0xb6: case 0xb7: case 0xbc: case 0xbd: case 0xbe: case 0xbf: case 0xc0: case 0xc2: case 0xc4: case 0xc5: case 0xc6: case 0xc7: case 0xc8: case 0xca: case 0xcb: case 0xcc: case 0xcd: case 0xce: case 0xcf: case 0xd0: case 0xd2: case 0xd3: case 0xd4: case 0xd5: case 0xd6: case 0xd7: case 0xd8: case 0xda: case 0xdb: case 0xdc: case 0xdd: case 0xde: case 0xdf: case 0xe0: case 0xe1: case 0xe2: case 0xe3: case 0xe4: case 0xe5: case 0xe6: case 0xe7: case 0xe8: case 0xe9: case 0xea: case 0xeb: case 0xec: case 0xed: case 0xee: case 0xef: case 0xf0: case 0xf1: case 0xf2: case 0xf4: case 0xf5: case 0xf6: case 0xf7: case 0xf8: case 0xf9: case 0xfa: case 0xfb: case 0xfc: case 0xfd: case 0xfe: case 0xff: { int c = nop(); NEXT; } case 0x40: { int c = in_R_c(); NEXT; } case 0x48: { int c = in_R_c(); NEXT; } case 0x50: { int c = in_R_c(); NEXT; } case 0x58: { int c = in_R_c(); NEXT; } case 0x60: { int c = in_R_c(); NEXT; } case 0x68: { int c = in_R_c(); NEXT; } case 0x70: { int c = in_R_c(); NEXT; } case 0x78: { int c = in_R_c(); NEXT; } case 0x41: { int c = out_c_R(); NEXT; } case 0x49: { int c = out_c_R(); NEXT; } case 0x51: { int c = out_c_R(); NEXT; } case 0x59: { int c = out_c_R(); NEXT; } case 0x61: { int c = out_c_R(); NEXT; } case 0x69: { int c = out_c_R(); NEXT; } case 0x71: { int c = out_c_0(); NEXT; } case 0x79: { int c = out_c_R(); NEXT; } case 0x42: { int c = sbc_hl_SS(); NEXT; } case 0x52: { int c = sbc_hl_SS(); NEXT; } case 0x62: { int c = sbc_hl_hl (); NEXT; } case 0x72: { int c = sbc_hl_SS(); NEXT; } case 0x4a: { int c = adc_hl_SS(); NEXT; } case 0x5a: { int c = adc_hl_SS(); NEXT; } case 0x6a: { int c = adc_hl_hl (); NEXT; } case 0x7a: { int c = adc_hl_SS(); NEXT; } case 0x43: { int c = ld_xword_SS_ED(); NEXT; } case 0x53: { int c = ld_xword_SS_ED(); NEXT; } case 0x63: { int c = ld_xword_SS_ED(); NEXT; } case 0x73: { int c = ld_xword_SS_ED(); NEXT; } case 0x4b: { int c = ld_SS_xword_ED(); NEXT; } case 0x5b: { int c = ld_SS_xword_ED(); NEXT; } case 0x6b: { int c = ld_SS_xword_ED(); NEXT; } case 0x7b: { int c = ld_SS_xword_ED(); NEXT; } case 0x47: { int c = ld_i_a(); NEXT; } case 0x4f: { int c = ld_r_a(); NEXT; } case 0x57: { int c = ld_a_IR(); if (T::isR800()) { NEXT; } else { NEXT_STOP; }} case 0x5f: { int c = ld_a_IR(); if (T::isR800()) { NEXT; } else { NEXT_STOP; }} case 0x67: { int c = rrd(); NEXT; } case 0x6f: { int c = rld(); NEXT; } case 0x45: case 0x4d: case 0x55: case 0x5d: case 0x65: case 0x6d: case 0x75: case 0x7d: { int c = retn(); NEXT_STOP; } case 0x46: case 0x4e: case 0x66: case 0x6e: { int c = im_N<0>(); NEXT; } case 0x56: case 0x76: { int c = im_N<1>(); NEXT; } case 0x5e: case 0x7e: { int c = im_N<2>(); NEXT; } case 0x44: case 0x4c: case 0x54: case 0x5c: case 0x64: case 0x6c: case 0x74: case 0x7c: { int c = neg(); NEXT; } case 0xa0: { int c = ldi(); NEXT; } case 0xa1: { int c = cpi(); NEXT; } case 0xa2: { int c = ini(); NEXT; } case 0xa3: { int c = outi(); NEXT; } case 0xa8: { int c = ldd(); NEXT; } case 0xa9: { int c = cpd(); NEXT; } case 0xaa: { int c = ind(); NEXT; } case 0xab: { int c = outd(); NEXT; } case 0xb0: { int c = ldir(); NEXT; } case 0xb1: { int c = cpir(); NEXT; } case 0xb2: { int c = inir(); NEXT; } case 0xb3: { int c = otir(); NEXT; } case 0xb8: { int c = lddr(); NEXT; } case 0xb9: { int c = cpdr(); NEXT; } case 0xba: { int c = indr(); NEXT; } case 0xbb: { int c = otdr(); NEXT; } case 0xc1: { int c = T::isR800() ? mulub_a_R() : nop(); NEXT; } case 0xc9: { int c = T::isR800() ? mulub_a_R() : nop(); NEXT; } case 0xd1: { int c = T::isR800() ? mulub_a_R() : nop(); NEXT; } case 0xd9: { int c = T::isR800() ? mulub_a_R() : nop(); NEXT; } case 0xc3: { int c = T::isR800() ? muluw_hl_SS() : nop(); NEXT; } case 0xf3: { int c = T::isR800() ? muluw_hl_SS() : nop(); NEXT; } default: UNREACHABLE; return; } } opDD_2: CASE(DD) { byte opcodeDD = RDMEM_OPCODE(T::CC_DD + T::CC_MAIN); incR(1); switch (opcodeDD) { case 0x00: // nop(); case 0x01: // ld_bc_word(); case 0x02: // ld_xbc_a(); case 0x03: // inc_bc(); case 0x04: // inc_b(); case 0x05: // dec_b(); case 0x06: // ld_b_byte(); case 0x07: // rlca(); case 0x08: // ex_af_af(); case 0x0a: // ld_a_xbc(); case 0x0b: // dec_bc(); case 0x0c: // inc_c(); case 0x0d: // dec_c(); case 0x0e: // ld_c_byte(); case 0x0f: // rrca(); case 0x10: // djnz(); case 0x11: // ld_de_word(); case 0x12: // ld_xde_a(); case 0x13: // inc_de(); case 0x14: // inc_d(); case 0x15: // dec_d(); case 0x16: // ld_d_byte(); case 0x17: // rla(); case 0x18: // jr(); case 0x1a: // ld_a_xde(); case 0x1b: // dec_de(); case 0x1c: // inc_e(); case 0x1d: // dec_e(); case 0x1e: // ld_e_byte(); case 0x1f: // rra(); case 0x20: // jr_nz(); case 0x27: // daa(); case 0x28: // jr_z(); case 0x2f: // cpl(); case 0x30: // jr_nc(); case 0x31: // ld_sp_word(); case 0x32: // ld_xbyte_a(); case 0x33: // inc_sp(); case 0x37: // scf(); case 0x38: // jr_c(); case 0x3a: // ld_a_xbyte(); case 0x3b: // dec_sp(); case 0x3c: // inc_a(); case 0x3d: // dec_a(); case 0x3e: // ld_a_byte(); case 0x3f: // ccf(); case 0x40: // ld_b_b(); case 0x41: // ld_b_c(); case 0x42: // ld_b_d(); case 0x43: // ld_b_e(); case 0x47: // ld_b_a(); case 0x48: // ld_c_b(); case 0x49: // ld_c_c(); case 0x4a: // ld_c_d(); case 0x4b: // ld_c_e(); case 0x4f: // ld_c_a(); case 0x50: // ld_d_b(); case 0x51: // ld_d_c(); case 0x52: // ld_d_d(); case 0x53: // ld_d_e(); case 0x57: // ld_d_a(); case 0x58: // ld_e_b(); case 0x59: // ld_e_c(); case 0x5a: // ld_e_d(); case 0x5b: // ld_e_e(); case 0x5f: // ld_e_a(); case 0x64: // ld_ixh_ixh(); == nop case 0x6d: // ld_ixl_ixl(); == nop case 0x76: // halt(); case 0x78: // ld_a_b(); case 0x79: // ld_a_c(); case 0x7a: // ld_a_d(); case 0x7b: // ld_a_e(); case 0x7f: // ld_a_a(); case 0x80: // add_a_b(); case 0x81: // add_a_c(); case 0x82: // add_a_d(); case 0x83: // add_a_e(); case 0x87: // add_a_a(); case 0x88: // adc_a_b(); case 0x89: // adc_a_c(); case 0x8a: // adc_a_d(); case 0x8b: // adc_a_e(); case 0x8f: // adc_a_a(); case 0x90: // sub_b(); case 0x91: // sub_c(); case 0x92: // sub_d(); case 0x93: // sub_e(); case 0x97: // sub_a(); case 0x98: // sbc_a_b(); case 0x99: // sbc_a_c(); case 0x9a: // sbc_a_d(); case 0x9b: // sbc_a_e(); case 0x9f: // sbc_a_a(); case 0xa0: // and_b(); case 0xa1: // and_c(); case 0xa2: // and_d(); case 0xa3: // and_e(); case 0xa7: // and_a(); case 0xa8: // xor_b(); case 0xa9: // xor_c(); case 0xaa: // xor_d(); case 0xab: // xor_e(); case 0xaf: // xor_a(); case 0xb0: // or_b(); case 0xb1: // or_c(); case 0xb2: // or_d(); case 0xb3: // or_e(); case 0xb7: // or_a(); case 0xb8: // cp_b(); case 0xb9: // cp_c(); case 0xba: // cp_d(); case 0xbb: // cp_e(); case 0xbf: // cp_a(); case 0xc0: // ret_nz(); case 0xc1: // pop_bc(); case 0xc2: // jp_nz(); case 0xc3: // jp(); case 0xc4: // call_nz(); case 0xc5: // push_bc(); case 0xc6: // add_a_byte(); case 0xc7: // rst_00(); case 0xc8: // ret_z(); case 0xc9: // ret(); case 0xca: // jp_z(); case 0xcc: // call_z(); case 0xcd: // call(); case 0xce: // adc_a_byte(); case 0xcf: // rst_08(); case 0xd0: // ret_nc(); case 0xd1: // pop_de(); case 0xd2: // jp_nc(); case 0xd3: // out_byte_a(); case 0xd4: // call_nc(); case 0xd5: // push_de(); case 0xd6: // sub_byte(); case 0xd7: // rst_10(); case 0xd8: // ret_c(); case 0xd9: // exx(); case 0xda: // jp_c(); case 0xdb: // in_a_byte(); case 0xdc: // call_c(); case 0xde: // sbc_a_byte(); case 0xdf: // rst_18(); case 0xe0: // ret_po(); case 0xe2: // jp_po(); case 0xe4: // call_po(); case 0xe6: // and_byte(); case 0xe7: // rst_20(); case 0xe8: // ret_pe(); case 0xea: // jp_pe(); case 0xeb: // ex_de_hl(); case 0xec: // call_pe(); case 0xed: // ed(); case 0xee: // xor_byte(); case 0xef: // rst_28(); case 0xf0: // ret_p(); case 0xf1: // pop_af(); case 0xf2: // jp_p(); case 0xf3: // di(); case 0xf4: // call_p(); case 0xf5: // push_af(); case 0xf6: // or_byte(); case 0xf7: // rst_30(); case 0xf8: // ret_m(); case 0xfa: // jp_m(); case 0xfb: // ei(); case 0xfc: // call_m(); case 0xfe: // cp_byte(); case 0xff: // rst_38(); if (T::isR800()) { int c = T::CC_DD + nop(); NEXT; } else { T::add(T::CC_DD); #ifdef USE_COMPUTED_GOTO goto *(opcodeTable[opcodeDD]); #else opcodeMain = opcodeDD; goto switchopcode; #endif } case 0x09: { int c = add_SS_TT(); NEXT; } case 0x19: { int c = add_SS_TT(); NEXT; } case 0x29: { int c = add_SS_SS(); NEXT; } case 0x39: { int c = add_SS_TT(); NEXT; } case 0x21: { int c = ld_SS_word(); NEXT; } case 0x22: { int c = ld_xword_SS(); NEXT; } case 0x2a: { int c = ld_SS_xword(); NEXT; } case 0x23: { int c = inc_SS(); NEXT; } case 0x2b: { int c = dec_SS(); NEXT; } case 0x24: { int c = inc_R(); NEXT; } case 0x2c: { int c = inc_R(); NEXT; } case 0x25: { int c = dec_R(); NEXT; } case 0x2d: { int c = dec_R(); NEXT; } case 0x26: { int c = ld_R_byte(); NEXT; } case 0x2e: { int c = ld_R_byte(); NEXT; } case 0x34: { int c = inc_xix(); NEXT; } case 0x35: { int c = dec_xix(); NEXT; } case 0x36: { int c = ld_xix_byte(); NEXT; } case 0x44: { int c = ld_R_R(); NEXT; } case 0x45: { int c = ld_R_R(); NEXT; } case 0x4c: { int c = ld_R_R(); NEXT; } case 0x4d: { int c = ld_R_R(); NEXT; } case 0x54: { int c = ld_R_R(); NEXT; } case 0x55: { int c = ld_R_R(); NEXT; } case 0x5c: { int c = ld_R_R(); NEXT; } case 0x5d: { int c = ld_R_R(); NEXT; } case 0x7c: { int c = ld_R_R(); NEXT; } case 0x7d: { int c = ld_R_R(); NEXT; } case 0x60: { int c = ld_R_R(); NEXT; } case 0x61: { int c = ld_R_R(); NEXT; } case 0x62: { int c = ld_R_R(); NEXT; } case 0x63: { int c = ld_R_R(); NEXT; } case 0x65: { int c = ld_R_R(); NEXT; } case 0x67: { int c = ld_R_R(); NEXT; } case 0x68: { int c = ld_R_R(); NEXT; } case 0x69: { int c = ld_R_R(); NEXT; } case 0x6a: { int c = ld_R_R(); NEXT; } case 0x6b: { int c = ld_R_R(); NEXT; } case 0x6c: { int c = ld_R_R(); NEXT; } case 0x6f: { int c = ld_R_R(); NEXT; } case 0x70: { int c = ld_xix_R(); NEXT; } case 0x71: { int c = ld_xix_R(); NEXT; } case 0x72: { int c = ld_xix_R(); NEXT; } case 0x73: { int c = ld_xix_R(); NEXT; } case 0x74: { int c = ld_xix_R(); NEXT; } case 0x75: { int c = ld_xix_R(); NEXT; } case 0x77: { int c = ld_xix_R(); NEXT; } case 0x46: { int c = ld_R_xix(); NEXT; } case 0x4e: { int c = ld_R_xix(); NEXT; } case 0x56: { int c = ld_R_xix(); NEXT; } case 0x5e: { int c = ld_R_xix(); NEXT; } case 0x66: { int c = ld_R_xix(); NEXT; } case 0x6e: { int c = ld_R_xix(); NEXT; } case 0x7e: { int c = ld_R_xix(); NEXT; } case 0x84: { int c = add_a_R(); NEXT; } case 0x85: { int c = add_a_R(); NEXT; } case 0x86: { int c = add_a_xix(); NEXT; } case 0x8c: { int c = adc_a_R(); NEXT; } case 0x8d: { int c = adc_a_R(); NEXT; } case 0x8e: { int c = adc_a_xix(); NEXT; } case 0x94: { int c = sub_R(); NEXT; } case 0x95: { int c = sub_R(); NEXT; } case 0x96: { int c = sub_xix(); NEXT; } case 0x9c: { int c = sbc_a_R(); NEXT; } case 0x9d: { int c = sbc_a_R(); NEXT; } case 0x9e: { int c = sbc_a_xix(); NEXT; } case 0xa4: { int c = and_R(); NEXT; } case 0xa5: { int c = and_R(); NEXT; } case 0xa6: { int c = and_xix(); NEXT; } case 0xac: { int c = xor_R(); NEXT; } case 0xad: { int c = xor_R(); NEXT; } case 0xae: { int c = xor_xix(); NEXT; } case 0xb4: { int c = or_R(); NEXT; } case 0xb5: { int c = or_R(); NEXT; } case 0xb6: { int c = or_xix(); NEXT; } case 0xbc: { int c = cp_R(); NEXT; } case 0xbd: { int c = cp_R(); NEXT; } case 0xbe: { int c = cp_xix(); NEXT; } case 0xe1: { int c = pop_SS (); NEXT; } case 0xe5: { int c = push_SS(); NEXT; } case 0xe3: { int c = ex_xsp_SS(); NEXT; } case 0xe9: { int c = jp_SS(); NEXT; } case 0xf9: { int c = ld_sp_SS(); NEXT; } case 0xcb: ixy = getIX(); goto xx_cb; case 0xdd: T::add(T::CC_DD); goto opDD_2; case 0xfd: T::add(T::CC_DD); goto opFD_2; default: UNREACHABLE; return; } } opFD_2: CASE(FD) { byte opcodeFD = RDMEM_OPCODE(T::CC_DD + T::CC_MAIN); incR(1); switch (opcodeFD) { case 0x00: // nop(); case 0x01: // ld_bc_word(); case 0x02: // ld_xbc_a(); case 0x03: // inc_bc(); case 0x04: // inc_b(); case 0x05: // dec_b(); case 0x06: // ld_b_byte(); case 0x07: // rlca(); case 0x08: // ex_af_af(); case 0x0a: // ld_a_xbc(); case 0x0b: // dec_bc(); case 0x0c: // inc_c(); case 0x0d: // dec_c(); case 0x0e: // ld_c_byte(); case 0x0f: // rrca(); case 0x10: // djnz(); case 0x11: // ld_de_word(); case 0x12: // ld_xde_a(); case 0x13: // inc_de(); case 0x14: // inc_d(); case 0x15: // dec_d(); case 0x16: // ld_d_byte(); case 0x17: // rla(); case 0x18: // jr(); case 0x1a: // ld_a_xde(); case 0x1b: // dec_de(); case 0x1c: // inc_e(); case 0x1d: // dec_e(); case 0x1e: // ld_e_byte(); case 0x1f: // rra(); case 0x20: // jr_nz(); case 0x27: // daa(); case 0x28: // jr_z(); case 0x2f: // cpl(); case 0x30: // jr_nc(); case 0x31: // ld_sp_word(); case 0x32: // ld_xbyte_a(); case 0x33: // inc_sp(); case 0x37: // scf(); case 0x38: // jr_c(); case 0x3a: // ld_a_xbyte(); case 0x3b: // dec_sp(); case 0x3c: // inc_a(); case 0x3d: // dec_a(); case 0x3e: // ld_a_byte(); case 0x3f: // ccf(); case 0x40: // ld_b_b(); case 0x41: // ld_b_c(); case 0x42: // ld_b_d(); case 0x43: // ld_b_e(); case 0x47: // ld_b_a(); case 0x48: // ld_c_b(); case 0x49: // ld_c_c(); case 0x4a: // ld_c_d(); case 0x4b: // ld_c_e(); case 0x4f: // ld_c_a(); case 0x50: // ld_d_b(); case 0x51: // ld_d_c(); case 0x52: // ld_d_d(); case 0x53: // ld_d_e(); case 0x57: // ld_d_a(); case 0x58: // ld_e_b(); case 0x59: // ld_e_c(); case 0x5a: // ld_e_d(); case 0x5b: // ld_e_e(); case 0x5f: // ld_e_a(); case 0x64: // ld_ixh_ixh(); == nop case 0x6d: // ld_ixl_ixl(); == nop case 0x76: // halt(); case 0x78: // ld_a_b(); case 0x79: // ld_a_c(); case 0x7a: // ld_a_d(); case 0x7b: // ld_a_e(); case 0x7f: // ld_a_a(); case 0x80: // add_a_b(); case 0x81: // add_a_c(); case 0x82: // add_a_d(); case 0x83: // add_a_e(); case 0x87: // add_a_a(); case 0x88: // adc_a_b(); case 0x89: // adc_a_c(); case 0x8a: // adc_a_d(); case 0x8b: // adc_a_e(); case 0x8f: // adc_a_a(); case 0x90: // sub_b(); case 0x91: // sub_c(); case 0x92: // sub_d(); case 0x93: // sub_e(); case 0x97: // sub_a(); case 0x98: // sbc_a_b(); case 0x99: // sbc_a_c(); case 0x9a: // sbc_a_d(); case 0x9b: // sbc_a_e(); case 0x9f: // sbc_a_a(); case 0xa0: // and_b(); case 0xa1: // and_c(); case 0xa2: // and_d(); case 0xa3: // and_e(); case 0xa7: // and_a(); case 0xa8: // xor_b(); case 0xa9: // xor_c(); case 0xaa: // xor_d(); case 0xab: // xor_e(); case 0xaf: // xor_a(); case 0xb0: // or_b(); case 0xb1: // or_c(); case 0xb2: // or_d(); case 0xb3: // or_e(); case 0xb7: // or_a(); case 0xb8: // cp_b(); case 0xb9: // cp_c(); case 0xba: // cp_d(); case 0xbb: // cp_e(); case 0xbf: // cp_a(); case 0xc0: // ret_nz(); case 0xc1: // pop_bc(); case 0xc2: // jp_nz(); case 0xc3: // jp(); case 0xc4: // call_nz(); case 0xc5: // push_bc(); case 0xc6: // add_a_byte(); case 0xc7: // rst_00(); case 0xc8: // ret_z(); case 0xc9: // ret(); case 0xca: // jp_z(); case 0xcc: // call_z(); case 0xcd: // call(); case 0xce: // adc_a_byte(); case 0xcf: // rst_08(); case 0xd0: // ret_nc(); case 0xd1: // pop_de(); case 0xd2: // jp_nc(); case 0xd3: // out_byte_a(); case 0xd4: // call_nc(); case 0xd5: // push_de(); case 0xd6: // sub_byte(); case 0xd7: // rst_10(); case 0xd8: // ret_c(); case 0xd9: // exx(); case 0xda: // jp_c(); case 0xdb: // in_a_byte(); case 0xdc: // call_c(); case 0xde: // sbc_a_byte(); case 0xdf: // rst_18(); case 0xe0: // ret_po(); case 0xe2: // jp_po(); case 0xe4: // call_po(); case 0xe6: // and_byte(); case 0xe7: // rst_20(); case 0xe8: // ret_pe(); case 0xea: // jp_pe(); case 0xeb: // ex_de_hl(); case 0xec: // call_pe(); case 0xed: // ed(); case 0xee: // xor_byte(); case 0xef: // rst_28(); case 0xf0: // ret_p(); case 0xf1: // pop_af(); case 0xf2: // jp_p(); case 0xf3: // di(); case 0xf4: // call_p(); case 0xf5: // push_af(); case 0xf6: // or_byte(); case 0xf7: // rst_30(); case 0xf8: // ret_m(); case 0xfa: // jp_m(); case 0xfb: // ei(); case 0xfc: // call_m(); case 0xfe: // cp_byte(); case 0xff: // rst_38(); if (T::isR800()) { int c = T::CC_DD + nop(); NEXT; } else { T::add(T::CC_DD); #ifdef USE_COMPUTED_GOTO goto *(opcodeTable[opcodeFD]); #else opcodeMain = opcodeFD; goto switchopcode; #endif } case 0x09: { int c = add_SS_TT(); NEXT; } case 0x19: { int c = add_SS_TT(); NEXT; } case 0x29: { int c = add_SS_SS(); NEXT; } case 0x39: { int c = add_SS_TT(); NEXT; } case 0x21: { int c = ld_SS_word(); NEXT; } case 0x22: { int c = ld_xword_SS(); NEXT; } case 0x2a: { int c = ld_SS_xword(); NEXT; } case 0x23: { int c = inc_SS(); NEXT; } case 0x2b: { int c = dec_SS(); NEXT; } case 0x24: { int c = inc_R(); NEXT; } case 0x2c: { int c = inc_R(); NEXT; } case 0x25: { int c = dec_R(); NEXT; } case 0x2d: { int c = dec_R(); NEXT; } case 0x26: { int c = ld_R_byte(); NEXT; } case 0x2e: { int c = ld_R_byte(); NEXT; } case 0x34: { int c = inc_xix(); NEXT; } case 0x35: { int c = dec_xix(); NEXT; } case 0x36: { int c = ld_xix_byte(); NEXT; } case 0x44: { int c = ld_R_R(); NEXT; } case 0x45: { int c = ld_R_R(); NEXT; } case 0x4c: { int c = ld_R_R(); NEXT; } case 0x4d: { int c = ld_R_R(); NEXT; } case 0x54: { int c = ld_R_R(); NEXT; } case 0x55: { int c = ld_R_R(); NEXT; } case 0x5c: { int c = ld_R_R(); NEXT; } case 0x5d: { int c = ld_R_R(); NEXT; } case 0x7c: { int c = ld_R_R(); NEXT; } case 0x7d: { int c = ld_R_R(); NEXT; } case 0x60: { int c = ld_R_R(); NEXT; } case 0x61: { int c = ld_R_R(); NEXT; } case 0x62: { int c = ld_R_R(); NEXT; } case 0x63: { int c = ld_R_R(); NEXT; } case 0x65: { int c = ld_R_R(); NEXT; } case 0x67: { int c = ld_R_R(); NEXT; } case 0x68: { int c = ld_R_R(); NEXT; } case 0x69: { int c = ld_R_R(); NEXT; } case 0x6a: { int c = ld_R_R(); NEXT; } case 0x6b: { int c = ld_R_R(); NEXT; } case 0x6c: { int c = ld_R_R(); NEXT; } case 0x6f: { int c = ld_R_R(); NEXT; } case 0x70: { int c = ld_xix_R(); NEXT; } case 0x71: { int c = ld_xix_R(); NEXT; } case 0x72: { int c = ld_xix_R(); NEXT; } case 0x73: { int c = ld_xix_R(); NEXT; } case 0x74: { int c = ld_xix_R(); NEXT; } case 0x75: { int c = ld_xix_R(); NEXT; } case 0x77: { int c = ld_xix_R(); NEXT; } case 0x46: { int c = ld_R_xix(); NEXT; } case 0x4e: { int c = ld_R_xix(); NEXT; } case 0x56: { int c = ld_R_xix(); NEXT; } case 0x5e: { int c = ld_R_xix(); NEXT; } case 0x66: { int c = ld_R_xix(); NEXT; } case 0x6e: { int c = ld_R_xix(); NEXT; } case 0x7e: { int c = ld_R_xix(); NEXT; } case 0x84: { int c = add_a_R(); NEXT; } case 0x85: { int c = add_a_R(); NEXT; } case 0x86: { int c = add_a_xix(); NEXT; } case 0x8c: { int c = adc_a_R(); NEXT; } case 0x8d: { int c = adc_a_R(); NEXT; } case 0x8e: { int c = adc_a_xix(); NEXT; } case 0x94: { int c = sub_R(); NEXT; } case 0x95: { int c = sub_R(); NEXT; } case 0x96: { int c = sub_xix(); NEXT; } case 0x9c: { int c = sbc_a_R(); NEXT; } case 0x9d: { int c = sbc_a_R(); NEXT; } case 0x9e: { int c = sbc_a_xix(); NEXT; } case 0xa4: { int c = and_R(); NEXT; } case 0xa5: { int c = and_R(); NEXT; } case 0xa6: { int c = and_xix(); NEXT; } case 0xac: { int c = xor_R(); NEXT; } case 0xad: { int c = xor_R(); NEXT; } case 0xae: { int c = xor_xix(); NEXT; } case 0xb4: { int c = or_R(); NEXT; } case 0xb5: { int c = or_R(); NEXT; } case 0xb6: { int c = or_xix(); NEXT; } case 0xbc: { int c = cp_R(); NEXT; } case 0xbd: { int c = cp_R(); NEXT; } case 0xbe: { int c = cp_xix(); NEXT; } case 0xe1: { int c = pop_SS (); NEXT; } case 0xe5: { int c = push_SS(); NEXT; } case 0xe3: { int c = ex_xsp_SS(); NEXT; } case 0xe9: { int c = jp_SS(); NEXT; } case 0xf9: { int c = ld_sp_SS(); NEXT; } case 0xcb: ixy = getIY(); goto xx_cb; case 0xdd: T::add(T::CC_DD); goto opDD_2; case 0xfd: T::add(T::CC_DD); goto opFD_2; default: UNREACHABLE; return; } } #ifndef USE_COMPUTED_GOTO default: UNREACHABLE; return; } #endif xx_cb: { unsigned tmp = RD_WORD_PC(T::CC_DD + T::CC_DD_CB); offset ofst = tmp & 0xFF; unsigned addr = (ixy + ofst) & 0xFFFF; byte xxcb_opcode = tmp >> 8; switch (xxcb_opcode) { case 0x00: { int c = rlc_xix_R(addr); NEXT; } case 0x01: { int c = rlc_xix_R(addr); NEXT; } case 0x02: { int c = rlc_xix_R(addr); NEXT; } case 0x03: { int c = rlc_xix_R(addr); NEXT; } case 0x04: { int c = rlc_xix_R(addr); NEXT; } case 0x05: { int c = rlc_xix_R(addr); NEXT; } case 0x06: { int c = rlc_xix_R(addr); NEXT; } case 0x07: { int c = rlc_xix_R(addr); NEXT; } case 0x08: { int c = rrc_xix_R(addr); NEXT; } case 0x09: { int c = rrc_xix_R(addr); NEXT; } case 0x0a: { int c = rrc_xix_R(addr); NEXT; } case 0x0b: { int c = rrc_xix_R(addr); NEXT; } case 0x0c: { int c = rrc_xix_R(addr); NEXT; } case 0x0d: { int c = rrc_xix_R(addr); NEXT; } case 0x0e: { int c = rrc_xix_R(addr); NEXT; } case 0x0f: { int c = rrc_xix_R(addr); NEXT; } case 0x10: { int c = rl_xix_R(addr); NEXT; } case 0x11: { int c = rl_xix_R(addr); NEXT; } case 0x12: { int c = rl_xix_R(addr); NEXT; } case 0x13: { int c = rl_xix_R(addr); NEXT; } case 0x14: { int c = rl_xix_R(addr); NEXT; } case 0x15: { int c = rl_xix_R(addr); NEXT; } case 0x16: { int c = rl_xix_R(addr); NEXT; } case 0x17: { int c = rl_xix_R(addr); NEXT; } case 0x18: { int c = rr_xix_R(addr); NEXT; } case 0x19: { int c = rr_xix_R(addr); NEXT; } case 0x1a: { int c = rr_xix_R(addr); NEXT; } case 0x1b: { int c = rr_xix_R(addr); NEXT; } case 0x1c: { int c = rr_xix_R(addr); NEXT; } case 0x1d: { int c = rr_xix_R(addr); NEXT; } case 0x1e: { int c = rr_xix_R(addr); NEXT; } case 0x1f: { int c = rr_xix_R(addr); NEXT; } case 0x20: { int c = sla_xix_R(addr); NEXT; } case 0x21: { int c = sla_xix_R(addr); NEXT; } case 0x22: { int c = sla_xix_R(addr); NEXT; } case 0x23: { int c = sla_xix_R(addr); NEXT; } case 0x24: { int c = sla_xix_R(addr); NEXT; } case 0x25: { int c = sla_xix_R(addr); NEXT; } case 0x26: { int c = sla_xix_R(addr); NEXT; } case 0x27: { int c = sla_xix_R(addr); NEXT; } case 0x28: { int c = sra_xix_R(addr); NEXT; } case 0x29: { int c = sra_xix_R(addr); NEXT; } case 0x2a: { int c = sra_xix_R(addr); NEXT; } case 0x2b: { int c = sra_xix_R(addr); NEXT; } case 0x2c: { int c = sra_xix_R(addr); NEXT; } case 0x2d: { int c = sra_xix_R(addr); NEXT; } case 0x2e: { int c = sra_xix_R(addr); NEXT; } case 0x2f: { int c = sra_xix_R(addr); NEXT; } case 0x30: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x31: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x32: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x33: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x34: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x35: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x36: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x37: { int c = T::isR800() ? sll2() : sll_xix_R(addr); NEXT; } case 0x38: { int c = srl_xix_R(addr); NEXT; } case 0x39: { int c = srl_xix_R(addr); NEXT; } case 0x3a: { int c = srl_xix_R(addr); NEXT; } case 0x3b: { int c = srl_xix_R(addr); NEXT; } case 0x3c: { int c = srl_xix_R(addr); NEXT; } case 0x3d: { int c = srl_xix_R(addr); NEXT; } case 0x3e: { int c = srl_xix_R(addr); NEXT; } case 0x3f: { int c = srl_xix_R(addr); NEXT; } case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47: { int c = bit_N_xix<0>(addr); NEXT; } case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f: { int c = bit_N_xix<1>(addr); NEXT; } case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57: { int c = bit_N_xix<2>(addr); NEXT; } case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f: { int c = bit_N_xix<3>(addr); NEXT; } case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: { int c = bit_N_xix<4>(addr); NEXT; } case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: { int c = bit_N_xix<5>(addr); NEXT; } case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: { int c = bit_N_xix<6>(addr); NEXT; } case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: { int c = bit_N_xix<7>(addr); NEXT; } case 0x80: { int c = res_N_xix_R<0,B>(addr); NEXT; } case 0x81: { int c = res_N_xix_R<0,C>(addr); NEXT; } case 0x82: { int c = res_N_xix_R<0,D>(addr); NEXT; } case 0x83: { int c = res_N_xix_R<0,E>(addr); NEXT; } case 0x84: { int c = res_N_xix_R<0,H>(addr); NEXT; } case 0x85: { int c = res_N_xix_R<0,L>(addr); NEXT; } case 0x87: { int c = res_N_xix_R<0,A>(addr); NEXT; } case 0x88: { int c = res_N_xix_R<1,B>(addr); NEXT; } case 0x89: { int c = res_N_xix_R<1,C>(addr); NEXT; } case 0x8a: { int c = res_N_xix_R<1,D>(addr); NEXT; } case 0x8b: { int c = res_N_xix_R<1,E>(addr); NEXT; } case 0x8c: { int c = res_N_xix_R<1,H>(addr); NEXT; } case 0x8d: { int c = res_N_xix_R<1,L>(addr); NEXT; } case 0x8f: { int c = res_N_xix_R<1,A>(addr); NEXT; } case 0x90: { int c = res_N_xix_R<2,B>(addr); NEXT; } case 0x91: { int c = res_N_xix_R<2,C>(addr); NEXT; } case 0x92: { int c = res_N_xix_R<2,D>(addr); NEXT; } case 0x93: { int c = res_N_xix_R<2,E>(addr); NEXT; } case 0x94: { int c = res_N_xix_R<2,H>(addr); NEXT; } case 0x95: { int c = res_N_xix_R<2,L>(addr); NEXT; } case 0x97: { int c = res_N_xix_R<2,A>(addr); NEXT; } case 0x98: { int c = res_N_xix_R<3,B>(addr); NEXT; } case 0x99: { int c = res_N_xix_R<3,C>(addr); NEXT; } case 0x9a: { int c = res_N_xix_R<3,D>(addr); NEXT; } case 0x9b: { int c = res_N_xix_R<3,E>(addr); NEXT; } case 0x9c: { int c = res_N_xix_R<3,H>(addr); NEXT; } case 0x9d: { int c = res_N_xix_R<3,L>(addr); NEXT; } case 0x9f: { int c = res_N_xix_R<3,A>(addr); NEXT; } case 0xa0: { int c = res_N_xix_R<4,B>(addr); NEXT; } case 0xa1: { int c = res_N_xix_R<4,C>(addr); NEXT; } case 0xa2: { int c = res_N_xix_R<4,D>(addr); NEXT; } case 0xa3: { int c = res_N_xix_R<4,E>(addr); NEXT; } case 0xa4: { int c = res_N_xix_R<4,H>(addr); NEXT; } case 0xa5: { int c = res_N_xix_R<4,L>(addr); NEXT; } case 0xa7: { int c = res_N_xix_R<4,A>(addr); NEXT; } case 0xa8: { int c = res_N_xix_R<5,B>(addr); NEXT; } case 0xa9: { int c = res_N_xix_R<5,C>(addr); NEXT; } case 0xaa: { int c = res_N_xix_R<5,D>(addr); NEXT; } case 0xab: { int c = res_N_xix_R<5,E>(addr); NEXT; } case 0xac: { int c = res_N_xix_R<5,H>(addr); NEXT; } case 0xad: { int c = res_N_xix_R<5,L>(addr); NEXT; } case 0xaf: { int c = res_N_xix_R<5,A>(addr); NEXT; } case 0xb0: { int c = res_N_xix_R<6,B>(addr); NEXT; } case 0xb1: { int c = res_N_xix_R<6,C>(addr); NEXT; } case 0xb2: { int c = res_N_xix_R<6,D>(addr); NEXT; } case 0xb3: { int c = res_N_xix_R<6,E>(addr); NEXT; } case 0xb4: { int c = res_N_xix_R<6,H>(addr); NEXT; } case 0xb5: { int c = res_N_xix_R<6,L>(addr); NEXT; } case 0xb7: { int c = res_N_xix_R<6,A>(addr); NEXT; } case 0xb8: { int c = res_N_xix_R<7,B>(addr); NEXT; } case 0xb9: { int c = res_N_xix_R<7,C>(addr); NEXT; } case 0xba: { int c = res_N_xix_R<7,D>(addr); NEXT; } case 0xbb: { int c = res_N_xix_R<7,E>(addr); NEXT; } case 0xbc: { int c = res_N_xix_R<7,H>(addr); NEXT; } case 0xbd: { int c = res_N_xix_R<7,L>(addr); NEXT; } case 0xbf: { int c = res_N_xix_R<7,A>(addr); NEXT; } case 0x86: { int c = res_N_xix_R<0,DUMMY>(addr); NEXT; } case 0x8e: { int c = res_N_xix_R<1,DUMMY>(addr); NEXT; } case 0x96: { int c = res_N_xix_R<2,DUMMY>(addr); NEXT; } case 0x9e: { int c = res_N_xix_R<3,DUMMY>(addr); NEXT; } case 0xa6: { int c = res_N_xix_R<4,DUMMY>(addr); NEXT; } case 0xae: { int c = res_N_xix_R<5,DUMMY>(addr); NEXT; } case 0xb6: { int c = res_N_xix_R<6,DUMMY>(addr); NEXT; } case 0xbe: { int c = res_N_xix_R<7,DUMMY>(addr); NEXT; } case 0xc0: { int c = set_N_xix_R<0,B>(addr); NEXT; } case 0xc1: { int c = set_N_xix_R<0,C>(addr); NEXT; } case 0xc2: { int c = set_N_xix_R<0,D>(addr); NEXT; } case 0xc3: { int c = set_N_xix_R<0,E>(addr); NEXT; } case 0xc4: { int c = set_N_xix_R<0,H>(addr); NEXT; } case 0xc5: { int c = set_N_xix_R<0,L>(addr); NEXT; } case 0xc7: { int c = set_N_xix_R<0,A>(addr); NEXT; } case 0xc8: { int c = set_N_xix_R<1,B>(addr); NEXT; } case 0xc9: { int c = set_N_xix_R<1,C>(addr); NEXT; } case 0xca: { int c = set_N_xix_R<1,D>(addr); NEXT; } case 0xcb: { int c = set_N_xix_R<1,E>(addr); NEXT; } case 0xcc: { int c = set_N_xix_R<1,H>(addr); NEXT; } case 0xcd: { int c = set_N_xix_R<1,L>(addr); NEXT; } case 0xcf: { int c = set_N_xix_R<1,A>(addr); NEXT; } case 0xd0: { int c = set_N_xix_R<2,B>(addr); NEXT; } case 0xd1: { int c = set_N_xix_R<2,C>(addr); NEXT; } case 0xd2: { int c = set_N_xix_R<2,D>(addr); NEXT; } case 0xd3: { int c = set_N_xix_R<2,E>(addr); NEXT; } case 0xd4: { int c = set_N_xix_R<2,H>(addr); NEXT; } case 0xd5: { int c = set_N_xix_R<2,L>(addr); NEXT; } case 0xd7: { int c = set_N_xix_R<2,A>(addr); NEXT; } case 0xd8: { int c = set_N_xix_R<3,B>(addr); NEXT; } case 0xd9: { int c = set_N_xix_R<3,C>(addr); NEXT; } case 0xda: { int c = set_N_xix_R<3,D>(addr); NEXT; } case 0xdb: { int c = set_N_xix_R<3,E>(addr); NEXT; } case 0xdc: { int c = set_N_xix_R<3,H>(addr); NEXT; } case 0xdd: { int c = set_N_xix_R<3,L>(addr); NEXT; } case 0xdf: { int c = set_N_xix_R<3,A>(addr); NEXT; } case 0xe0: { int c = set_N_xix_R<4,B>(addr); NEXT; } case 0xe1: { int c = set_N_xix_R<4,C>(addr); NEXT; } case 0xe2: { int c = set_N_xix_R<4,D>(addr); NEXT; } case 0xe3: { int c = set_N_xix_R<4,E>(addr); NEXT; } case 0xe4: { int c = set_N_xix_R<4,H>(addr); NEXT; } case 0xe5: { int c = set_N_xix_R<4,L>(addr); NEXT; } case 0xe7: { int c = set_N_xix_R<4,A>(addr); NEXT; } case 0xe8: { int c = set_N_xix_R<5,B>(addr); NEXT; } case 0xe9: { int c = set_N_xix_R<5,C>(addr); NEXT; } case 0xea: { int c = set_N_xix_R<5,D>(addr); NEXT; } case 0xeb: { int c = set_N_xix_R<5,E>(addr); NEXT; } case 0xec: { int c = set_N_xix_R<5,H>(addr); NEXT; } case 0xed: { int c = set_N_xix_R<5,L>(addr); NEXT; } case 0xef: { int c = set_N_xix_R<5,A>(addr); NEXT; } case 0xf0: { int c = set_N_xix_R<6,B>(addr); NEXT; } case 0xf1: { int c = set_N_xix_R<6,C>(addr); NEXT; } case 0xf2: { int c = set_N_xix_R<6,D>(addr); NEXT; } case 0xf3: { int c = set_N_xix_R<6,E>(addr); NEXT; } case 0xf4: { int c = set_N_xix_R<6,H>(addr); NEXT; } case 0xf5: { int c = set_N_xix_R<6,L>(addr); NEXT; } case 0xf7: { int c = set_N_xix_R<6,A>(addr); NEXT; } case 0xf8: { int c = set_N_xix_R<7,B>(addr); NEXT; } case 0xf9: { int c = set_N_xix_R<7,C>(addr); NEXT; } case 0xfa: { int c = set_N_xix_R<7,D>(addr); NEXT; } case 0xfb: { int c = set_N_xix_R<7,E>(addr); NEXT; } case 0xfc: { int c = set_N_xix_R<7,H>(addr); NEXT; } case 0xfd: { int c = set_N_xix_R<7,L>(addr); NEXT; } case 0xff: { int c = set_N_xix_R<7,A>(addr); NEXT; } case 0xc6: { int c = set_N_xix_R<0,DUMMY>(addr); NEXT; } case 0xce: { int c = set_N_xix_R<1,DUMMY>(addr); NEXT; } case 0xd6: { int c = set_N_xix_R<2,DUMMY>(addr); NEXT; } case 0xde: { int c = set_N_xix_R<3,DUMMY>(addr); NEXT; } case 0xe6: { int c = set_N_xix_R<4,DUMMY>(addr); NEXT; } case 0xee: { int c = set_N_xix_R<5,DUMMY>(addr); NEXT; } case 0xf6: { int c = set_N_xix_R<6,DUMMY>(addr); NEXT; } case 0xfe: { int c = set_N_xix_R<7,DUMMY>(addr); NEXT; } default: UNREACHABLE; } } } template inline void CPUCore::cpuTracePre() { start_pc = getPC(); } template inline void CPUCore::cpuTracePost() { if (unlikely(tracingEnabled)) { cpuTracePost_slow(); } } template void CPUCore::cpuTracePost_slow() { byte opbuf[4]; string dasmOutput; dasm(*interface, start_pc, opbuf, dasmOutput, T::getTimeFast()); std::cout << std::setfill('0') << std::hex << std::setw(4) << start_pc << " : " << dasmOutput << " AF=" << std::setw(4) << getAF() << " BC=" << std::setw(4) << getBC() << " DE=" << std::setw(4) << getDE() << " HL=" << std::setw(4) << getHL() << " IX=" << std::setw(4) << getIX() << " IY=" << std::setw(4) << getIY() << " SP=" << std::setw(4) << getSP() << std::endl << std::dec; } template void CPUCore::executeSlow() { if (unlikely(false && nmiEdge)) { // Note: NMIs are disabled, see also raiseNMI() nmiEdge = false; nmi(); // NMI occured } else if (unlikely(IRQStatus && getIFF1() && !getAfterEI())) { // normal interrupt if (unlikely(getAfterLDAI())) { // HACK!!! // The 'ld a,i' or 'ld a,r' instruction copies the IFF2 // bit to the V flag. Though when the Z80 accepts an // IRQ directly after this instruction, the V flag is 0 // (instead of the expected value 1). This can probably // be explained if you look at the pipeline of the Z80. // But for speed reasons we implement it here as a // fix-up (a hack) in the IRQ routine. This behaviour // is actually a bug in the Z80. // Thanks to n_n for reporting this behaviour. I think // this was discovered by GuyveR800. Also thanks to // n_n for writing a test program that demonstrates // this quirk. // I also wrote a test program that demonstrates this // behaviour is the same whether 'ld a,i' is preceded // by a 'ei' instruction or not (so it's not caused by // the 'delayed IRQ acceptance of ei'). assert(getF() & V_FLAG); setF(getF() & ~V_FLAG); } IRQAccept.signal(); switch (getIM()) { case 0: irq0(); break; case 1: irq1(); break; case 2: irq2(); break; default: UNREACHABLE; } } else if (unlikely(getHALT())) { // in halt mode incR(T::advanceHalt(T::haltStates(), scheduler.getNext())); setSlowInstructions(); } else { assert(isSameAfter()); clearNextAfter(); cpuTracePre(); assert(T::limitReached()); // we want only one instruction executeInstructions(); cpuTracePost(); copyNextAfter(); } } template void CPUCore::execute(bool fastForward) { // In fast-forward mode, breakpoints, watchpoints or debug condtions // won't trigger. It is possible we already are in break mode, but // break is ignored in fast-forward mode. assert(fastForward || !interface->isBreaked()); if (fastForward) { interface->setFastForward(true); } execute2(fastForward); interface->setFastForward(false); } template void CPUCore::execute2(bool fastForward) { // note: Don't use getTimeFast() here, because 'once in a while' we // need to CPUClock::sync() to avoid overflow. // Should be done at least once per second (approx). So only // once in this method is enough. scheduler.schedule(T::getTime()); setSlowInstructions(); if (!fastForward && (interface->isContinue() || interface->isStep())) { // at least one instruction interface->setContinue(false); executeSlow(); scheduler.schedule(T::getTimeFast()); --slowInstructions; if (interface->isStep()) { interface->setStep(false); interface->doBreak(); return; } } // Note: we call scheduler _after_ executing the instruction and before // deciding between executeFast() and executeSlow() (because a // SyncPoint could set an IRQ and then we must choose executeSlow()) if (fastForward || (!interface->anyBreakPoints() && !tracingEnabled)) { // fast path, no breakpoints, no tracing while (!needExitCPULoop()) { if (slowInstructions) { --slowInstructions; executeSlow(); scheduler.schedule(T::getTimeFast()); } else { while (slowInstructions == 0) { T::enableLimit(); // does CPUClock::sync() if (likely(!T::limitReached())) { // multiple instructions assert(isSameAfter()); executeInstructions(); assert(isSameAfter()); } scheduler.schedule(T::getTimeFast()); if (needExitCPULoop()) return; } } } } else { while (!needExitCPULoop()) { if (interface->checkBreakPoints(getPC())) { assert(interface->isBreaked()); break; } if (slowInstructions == 0) { cpuTracePre(); assert(T::limitReached()); // only one instruction assert(isSameAfter()); executeInstructions(); assert(isSameAfter()); cpuTracePost(); } else { --slowInstructions; executeSlow(); } // Don't use getTimeFast() here, we need a call to // CPUClock::sync() 'once in a while'. (During a // reverse fast-forward this wasn't always the case). scheduler.schedule(T::getTime()); } } } template template ALWAYS_INLINE byte CPUCore::get8() const { if (R8 == A) { return getA(); } else if (R8 == F) { return getF(); } else if (R8 == B) { return getB(); } else if (R8 == C) { return getC(); } else if (R8 == D) { return getD(); } else if (R8 == E) { return getE(); } else if (R8 == H) { return getH(); } else if (R8 == L) { return getL(); } else if (R8 == IXH) { return getIXh(); } else if (R8 == IXL) { return getIXl(); } else if (R8 == IYH) { return getIYh(); } else if (R8 == IYL) { return getIYl(); } else if (R8 == REG_I) { return getI(); } else if (R8 == REG_R) { return getR(); } else if (R8 == DUMMY) { return 0; } else { UNREACHABLE; return 0; } } template template ALWAYS_INLINE unsigned CPUCore::get16() const { if (R16 == AF) { return getAF(); } else if (R16 == BC) { return getBC(); } else if (R16 == DE) { return getDE(); } else if (R16 == HL) { return getHL(); } else if (R16 == IX) { return getIX(); } else if (R16 == IY) { return getIY(); } else if (R16 == SP) { return getSP(); } else { UNREACHABLE; return 0; } } template template ALWAYS_INLINE void CPUCore::set8(byte x) { if (R8 == A) { setA(x); } else if (R8 == F) { setF(x); } else if (R8 == B) { setB(x); } else if (R8 == C) { setC(x); } else if (R8 == D) { setD(x); } else if (R8 == E) { setE(x); } else if (R8 == H) { setH(x); } else if (R8 == L) { setL(x); } else if (R8 == IXH) { setIXh(x); } else if (R8 == IXL) { setIXl(x); } else if (R8 == IYH) { setIYh(x); } else if (R8 == IYL) { setIYl(x); } else if (R8 == REG_I) { setI(x); } else if (R8 == REG_R) { setR(x); } else if (R8 == DUMMY) { /* nothing */ } else { UNREACHABLE; } } template template ALWAYS_INLINE void CPUCore::set16(unsigned x) { if (R16 == AF) { setAF(x); } else if (R16 == BC) { setBC(x); } else if (R16 == DE) { setDE(x); } else if (R16 == HL) { setHL(x); } else if (R16 == IX) { setIX(x); } else if (R16 == IY) { setIY(x); } else if (R16 == SP) { setSP(x); } else { UNREACHABLE; } } // LD r,r template template int CPUCore::ld_R_R() { set8(get8()); return T::CC_LD_R_R + EE; } // LD SP,ss template template int CPUCore::ld_sp_SS() { setSP(get16()); return T::CC_LD_SP_HL + EE; } // LD (ss),a template template int CPUCore::ld_SS_a() { T::setMemPtr((getA() << 8) | ((get16() + 1) & 0xFF)); WRMEM(get16(), getA(), T::CC_LD_SS_A_1); return T::CC_LD_SS_A; } // LD (HL),r template template int CPUCore::ld_xhl_R() { WRMEM(getHL(), get8(), T::CC_LD_HL_R_1); return T::CC_LD_HL_R; } // LD (IXY+e),r template template int CPUCore::ld_xix_R() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_LD_XIX_R_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); WRMEM(addr, get8(), T::CC_DD + T::CC_LD_XIX_R_2); return T::CC_DD + T::CC_LD_XIX_R; } // LD (HL),n template int CPUCore::ld_xhl_byte() { byte val = RDMEM_OPCODE(T::CC_LD_HL_N_1); WRMEM(getHL(), val, T::CC_LD_HL_N_2); return T::CC_LD_HL_N; } // LD (IXY+e),n template template int CPUCore::ld_xix_byte() { unsigned tmp = RD_WORD_PC(T::CC_DD + T::CC_LD_XIX_N_1); offset ofst = tmp & 0xFF; byte val = tmp >> 8; unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); WRMEM(addr, val, T::CC_DD + T::CC_LD_XIX_N_2); return T::CC_DD + T::CC_LD_XIX_N; } // LD (nn),A template int CPUCore::ld_xbyte_a() { unsigned x = RD_WORD_PC(T::CC_LD_NN_A_1); T::setMemPtr((getA() << 8) | ((x + 1) & 0xFF)); WRMEM(x, getA(), T::CC_LD_NN_A_2); return T::CC_LD_NN_A; } // LD (nn),ss template template inline int CPUCore::WR_NN_Y(unsigned reg) { unsigned addr = RD_WORD_PC(T::CC_LD_XX_HL_1 + EE); T::setMemPtr(addr + 1); WR_WORD(addr, reg, T::CC_LD_XX_HL_2 + EE); return T::CC_LD_XX_HL + EE; } template template int CPUCore::ld_xword_SS() { return WR_NN_Y(get16()); } template template int CPUCore::ld_xword_SS_ED() { return WR_NN_Y(get16()); } // LD A,(ss) template template int CPUCore::ld_a_SS() { T::setMemPtr(get16() + 1); setA(RDMEM(get16(), T::CC_LD_A_SS_1)); return T::CC_LD_A_SS; } // LD A,(nn) template int CPUCore::ld_a_xbyte() { unsigned addr = RD_WORD_PC(T::CC_LD_A_NN_1); T::setMemPtr(addr + 1); setA(RDMEM(addr, T::CC_LD_A_NN_2)); return T::CC_LD_A_NN; } // LD r,n template template int CPUCore::ld_R_byte() { set8(RDMEM_OPCODE(T::CC_LD_R_N_1 + EE)); return T::CC_LD_R_N + EE; } // LD r,(hl) template template int CPUCore::ld_R_xhl() { set8(RDMEM(getHL(), T::CC_LD_R_HL_1)); return T::CC_LD_R_HL; } // LD r,(IXY+e) template template int CPUCore::ld_R_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_LD_R_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); set8(RDMEM(addr, T::CC_DD + T::CC_LD_R_XIX_2)); return T::CC_DD + T::CC_LD_R_XIX; } // LD ss,(nn) template template inline unsigned CPUCore::RD_P_XX() { unsigned addr = RD_WORD_PC(T::CC_LD_HL_XX_1 + EE); T::setMemPtr(addr + 1); unsigned result = RD_WORD(addr, T::CC_LD_HL_XX_2 + EE); return result; } template template int CPUCore::ld_SS_xword() { set16(RD_P_XX()); return T::CC_LD_HL_XX + EE; } template template int CPUCore::ld_SS_xword_ED() { set16(RD_P_XX()); return T::CC_LD_HL_XX + T::EE_ED; } // LD ss,nn template template int CPUCore::ld_SS_word() { set16(RD_WORD_PC(T::CC_LD_SS_NN_1 + EE)); return T::CC_LD_SS_NN + EE; } // ADC A,r template inline void CPUCore::ADC(byte reg) { unsigned res = getA() + reg + ((getF() & C_FLAG) ? 1 : 0); byte f = ((res & 0x100) ? C_FLAG : 0) | ((getA() ^ res ^ reg) & H_FLAG) | (((getA() ^ res) & (reg ^ res) & 0x80) >> 5) | // V_FLAG 0; // N_FLAG if (T::isR800()) { f |= ZSTable[res & 0xFF]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSXYTable[res & 0xFF]; } setF(f); setA(res); } template inline int CPUCore::adc_a_a() { unsigned res = 2 * getA() + ((getF() & C_FLAG) ? 1 : 0); byte f = ((res & 0x100) ? C_FLAG : 0) | (res & H_FLAG) | (((getA() ^ res) & 0x80) >> 5) | // V_FLAG 0; // N_FLAG if (T::isR800()) { f |= ZSTable[res & 0xFF]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSXYTable[res & 0xFF]; } setF(f); setA(res); return T::CC_CP_R; } template template int CPUCore::adc_a_R() { ADC(get8()); return T::CC_CP_R + EE; } template int CPUCore::adc_a_byte() { ADC(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::adc_a_xhl() { ADC(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::adc_a_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); ADC(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // ADD A,r template inline void CPUCore::ADD(byte reg) { unsigned res = getA() + reg; byte f = ((res & 0x100) ? C_FLAG : 0) | ((getA() ^ res ^ reg) & H_FLAG) | (((getA() ^ res) & (reg ^ res) & 0x80) >> 5) | // V_FLAG 0; // N_FLAG if (T::isR800()) { f |= ZSTable[res & 0xFF]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSXYTable[res & 0xFF]; } setF(f); setA(res); } template inline int CPUCore::add_a_a() { unsigned res = 2 * getA(); byte f = ((res & 0x100) ? C_FLAG : 0) | (res & H_FLAG) | (((getA() ^ res) & 0x80) >> 5) | // V_FLAG 0; // N_FLAG if (T::isR800()) { f |= ZSTable[res & 0xFF]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSXYTable[res & 0xFF]; } setF(f); setA(res); return T::CC_CP_R; } template template int CPUCore::add_a_R() { ADD(get8()); return T::CC_CP_R + EE; } template int CPUCore::add_a_byte() { ADD(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::add_a_xhl() { ADD(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::add_a_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); ADD(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // AND r template inline void CPUCore::AND(byte reg) { setA(getA() & reg); byte f = 0; if (T::isR800()) { f |= ZSPHTable[getA()]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[getA()] | H_FLAG; } setF(f); } template int CPUCore::and_a() { byte f = 0; if (T::isR800()) { f |= ZSPHTable[getA()]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[getA()] | H_FLAG; } setF(f); return T::CC_CP_R; } template template int CPUCore::and_R() { AND(get8()); return T::CC_CP_R + EE; } template int CPUCore::and_byte() { AND(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::and_xhl() { AND(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::and_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); AND(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // CP r template inline void CPUCore::CP(byte reg) { unsigned q = getA() - reg; byte f = ZSTable[q & 0xFF] | ((q & 0x100) ? C_FLAG : 0) | N_FLAG | ((getA() ^ q ^ reg) & H_FLAG) | (((reg ^ getA()) & (getA() ^ q) & 0x80) >> 5); // V_FLAG if (T::isR800()) { f |= getF() & (X_FLAG | Y_FLAG); } else { f |= reg & (X_FLAG | Y_FLAG); // XY from operand, not from result } setF(f); } template int CPUCore::cp_a() { byte f = ZS0 | N_FLAG; if (T::isR800()) { f |= getF() & (X_FLAG | Y_FLAG); } else { f |= getA() & (X_FLAG | Y_FLAG); // XY from operand, not from result } setF(f); return T::CC_CP_R; } template template int CPUCore::cp_R() { CP(get8()); return T::CC_CP_R + EE; } template int CPUCore::cp_byte() { CP(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::cp_xhl() { CP(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::cp_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); CP(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // OR r template inline void CPUCore::OR(byte reg) { setA(getA() | reg); byte f = 0; if (T::isR800()) { f |= ZSPTable[getA()]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[getA()]; } setF(f); } template int CPUCore::or_a() { byte f = 0; if (T::isR800()) { f |= ZSPTable[getA()]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[getA()]; } setF(f); return T::CC_CP_R; } template template int CPUCore::or_R() { OR(get8()); return T::CC_CP_R + EE; } template int CPUCore::or_byte() { OR(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::or_xhl() { OR(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::or_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); OR(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // SBC A,r template inline void CPUCore::SBC(byte reg) { unsigned res = getA() - reg - ((getF() & C_FLAG) ? 1 : 0); byte f = ((res & 0x100) ? C_FLAG : 0) | N_FLAG | ((getA() ^ res ^ reg) & H_FLAG) | (((reg ^ getA()) & (getA() ^ res) & 0x80) >> 5); // V_FLAG if (T::isR800()) { f |= ZSTable[res & 0xFF]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSXYTable[res & 0xFF]; } setF(f); setA(res); } template int CPUCore::sbc_a_a() { if (T::isR800()) { word t = (getF() & C_FLAG) ? (255 * 256 | ZS255 | C_FLAG | H_FLAG | N_FLAG) : ( 0 * 256 | ZS0 | N_FLAG); setAF(t | (getF() & (X_FLAG | Y_FLAG))); } else { setAF((getF() & C_FLAG) ? (255 * 256 | ZSXY255 | C_FLAG | H_FLAG | N_FLAG) : ( 0 * 256 | ZSXY0 | N_FLAG)); } return T::CC_CP_R; } template template int CPUCore::sbc_a_R() { SBC(get8()); return T::CC_CP_R + EE; } template int CPUCore::sbc_a_byte() { SBC(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::sbc_a_xhl() { SBC(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::sbc_a_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); SBC(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // SUB r template inline void CPUCore::SUB(byte reg) { unsigned res = getA() - reg; byte f = ((res & 0x100) ? C_FLAG : 0) | N_FLAG | ((getA() ^ res ^ reg) & H_FLAG) | (((reg ^ getA()) & (getA() ^ res) & 0x80) >> 5); // V_FLAG if (T::isR800()) { f |= ZSTable[res & 0xFF]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSXYTable[res & 0xFF]; } setF(f); setA(res); } template int CPUCore::sub_a() { if (T::isR800()) { word t = 0 * 256 | ZS0 | N_FLAG; setAF(t | (getF() & (X_FLAG | Y_FLAG))); } else { setAF(0 * 256 | ZSXY0 | N_FLAG); } return T::CC_CP_R; } template template int CPUCore::sub_R() { SUB(get8()); return T::CC_CP_R + EE; } template int CPUCore::sub_byte() { SUB(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::sub_xhl() { SUB(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::sub_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); SUB(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // XOR r template inline void CPUCore::XOR(byte reg) { setA(getA() ^ reg); byte f = 0; if (T::isR800()) { f |= ZSPTable[getA()]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[getA()]; } setF(f); } template int CPUCore::xor_a() { if (T::isR800()) { word t = 0 * 256 + ZSP0; setAF(t | (getF() & (X_FLAG | Y_FLAG))); } else { setAF(0 * 256 + ZSPXY0); } return T::CC_CP_R; } template template int CPUCore::xor_R() { XOR(get8()); return T::CC_CP_R + EE; } template int CPUCore::xor_byte() { XOR(RDMEM_OPCODE(T::CC_CP_N_1)); return T::CC_CP_N; } template int CPUCore::xor_xhl() { XOR(RDMEM(getHL(), T::CC_CP_XHL_1)); return T::CC_CP_XHL; } template template int CPUCore::xor_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_CP_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); XOR(RDMEM(addr, T::CC_DD + T::CC_CP_XIX_2)); return T::CC_DD + T::CC_CP_XIX; } // DEC r template inline byte CPUCore::DEC(byte reg) { byte res = reg - 1; byte f = ((reg & ~res & 0x80) >> 5) | // V_FLAG (((res & 0x0F) + 1) & H_FLAG) | N_FLAG; if (T::isR800()) { f |= getF() & (C_FLAG | X_FLAG | Y_FLAG); f |= ZSTable[res]; } else { f |= getF() & C_FLAG; f |= ZSXYTable[res]; } setF(f); return res; } template template int CPUCore::dec_R() { set8(DEC(get8())); return T::CC_INC_R + EE; } template template inline int CPUCore::DEC_X(unsigned x) { byte val = DEC(RDMEM(x, T::CC_INC_XHL_1 + EE)); WRMEM(x, val, T::CC_INC_XHL_2 + EE); return T::CC_INC_XHL + EE; } template int CPUCore::dec_xhl() { return DEC_X<0>(getHL()); } template template int CPUCore::dec_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_INC_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); return DEC_X(addr); } // INC r template inline byte CPUCore::INC(byte reg) { reg++; byte f = ((reg & -reg & 0x80) >> 5) | // V_FLAG (((reg & 0x0F) - 1) & H_FLAG) | 0; // N_FLAG if (T::isR800()) { f |= getF() & (C_FLAG | X_FLAG | Y_FLAG); f |= ZSTable[reg]; } else { f |= getF() & C_FLAG; f |= ZSXYTable[reg]; } setF(f); return reg; } template template int CPUCore::inc_R() { set8(INC(get8())); return T::CC_INC_R + EE; } template template inline int CPUCore::INC_X(unsigned x) { byte val = INC(RDMEM(x, T::CC_INC_XHL_1 + EE)); WRMEM(x, val, T::CC_INC_XHL_2 + EE); return T::CC_INC_XHL + EE; } template int CPUCore::inc_xhl() { return INC_X<0>(getHL()); } template template int CPUCore::inc_xix() { offset ofst = RDMEM_OPCODE(T::CC_DD + T::CC_INC_XIX_1); unsigned addr = (get16() + ofst) & 0xFFFF; T::setMemPtr(addr); return INC_X(addr); } // ADC HL,ss template template inline int CPUCore::adc_hl_SS() { unsigned reg = get16(); T::setMemPtr(getHL() + 1); unsigned res = getHL() + reg + ((getF() & C_FLAG) ? 1 : 0); byte f = (res >> 16) | // C_FLAG 0; // N_FLAG if (T::isR800()) { f |= getF() & (X_FLAG | Y_FLAG); } if (res & 0xFFFF) { f |= ((getHL() ^ res ^ reg) >> 8) & H_FLAG; f |= 0; // Z_FLAG f |= ((getHL() ^ res) & (reg ^ res) & 0x8000) >> 13; // V_FLAG if (T::isR800()) { f |= (res >> 8) & S_FLAG; } else { f |= (res >> 8) & (S_FLAG | X_FLAG | Y_FLAG); } } else { f |= ((getHL() ^ reg) >> 8) & H_FLAG; f |= Z_FLAG; f |= (getHL() & reg & 0x8000) >> 13; // V_FLAG f |= 0; // S_FLAG (X_FLAG Y_FLAG) } setF(f); setHL(res); return T::CC_ADC_HL_SS; } template int CPUCore::adc_hl_hl() { T::setMemPtr(getHL() + 1); unsigned res = 2 * getHL() + ((getF() & C_FLAG) ? 1 : 0); byte f = (res >> 16) | // C_FLAG 0; // N_FLAG if (T::isR800()) { f |= getF() & (X_FLAG | Y_FLAG); } if (res & 0xFFFF) { f |= 0; // Z_FLAG f |= ((getHL() ^ res) & 0x8000) >> 13; // V_FLAG if (T::isR800()) { f |= (res >> 8) & (H_FLAG | S_FLAG); } else { f |= (res >> 8) & (H_FLAG | S_FLAG | X_FLAG | Y_FLAG); } } else { f |= Z_FLAG; f |= (getHL() & 0x8000) >> 13; // V_FLAG f |= 0; // H_FLAG S_FLAG (X_FLAG Y_FLAG) } setF(f); setHL(res); return T::CC_ADC_HL_SS; } // ADD HL/IX/IY,ss template template int CPUCore::add_SS_TT() { unsigned reg1 = get16(); unsigned reg2 = get16(); T::setMemPtr(reg1 + 1); unsigned res = reg1 + reg2; byte f = (((reg1 ^ res ^ reg2) >> 8) & H_FLAG) | (res >> 16) | // C_FLAG 0; // N_FLAG if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | V_FLAG | X_FLAG | Y_FLAG); } else { f |= getF() & (S_FLAG | Z_FLAG | V_FLAG); f |= (res >> 8) & (X_FLAG | Y_FLAG); } setF(f); set16(res & 0xFFFF); return T::CC_ADD_HL_SS + EE; } template template int CPUCore::add_SS_SS() { unsigned reg = get16(); T::setMemPtr(reg + 1); unsigned res = 2 * reg; byte f = (res >> 16) | // C_FLAG 0; // N_FLAG if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | V_FLAG | X_FLAG | Y_FLAG); f |= (res >> 8) & H_FLAG; } else { f |= getF() & (S_FLAG | Z_FLAG | V_FLAG); f |= (res >> 8) & (H_FLAG | X_FLAG | Y_FLAG); } setF(f); set16(res & 0xFFFF); return T::CC_ADD_HL_SS + EE; } // SBC HL,ss template template inline int CPUCore::sbc_hl_SS() { unsigned reg = get16(); T::setMemPtr(getHL() + 1); unsigned res = getHL() - reg - ((getF() & C_FLAG) ? 1 : 0); byte f = ((res & 0x10000) ? C_FLAG : 0) | N_FLAG; if (T::isR800()) { f |= getF() & (X_FLAG | Y_FLAG); } if (res & 0xFFFF) { f |= ((getHL() ^ res ^ reg) >> 8) & H_FLAG; f |= 0; // Z_FLAG f |= ((reg ^ getHL()) & (getHL() ^ res) & 0x8000) >> 13; // V_FLAG if (T::isR800()) { f |= (res >> 8) & S_FLAG; } else { f |= (res >> 8) & (S_FLAG | X_FLAG | Y_FLAG); } } else { f |= ((getHL() ^ reg) >> 8) & H_FLAG; f |= Z_FLAG; f |= ((reg ^ getHL()) & getHL() & 0x8000) >> 13; // V_FLAG f |= 0; // S_FLAG (X_FLAG Y_FLAG) } setF(f); setHL(res); return T::CC_ADC_HL_SS; } template int CPUCore::sbc_hl_hl() { T::setMemPtr(getHL() + 1); byte f = T::isR800() ? (getF() & (X_FLAG | Y_FLAG)) : 0; if (getF() & C_FLAG) { f |= C_FLAG | H_FLAG | S_FLAG | N_FLAG; if (!T::isR800()) { f |= X_FLAG | Y_FLAG; } setHL(0xFFFF); } else { f |= Z_FLAG | N_FLAG; setHL(0); } setF(f); return T::CC_ADC_HL_SS; } // DEC ss template template int CPUCore::dec_SS() { set16(get16() - 1); return T::CC_INC_SS + EE; } // INC ss template template int CPUCore::inc_SS() { set16(get16() + 1); return T::CC_INC_SS + EE; } // BIT n,r template template int CPUCore::bit_N_R() { byte reg = get8(); byte f = 0; // N_FLAG if (T::isR800()) { // this is very different from Z80 (not only XY flags) f |= getF() & (S_FLAG | V_FLAG | C_FLAG | X_FLAG | Y_FLAG); f |= H_FLAG; f |= (reg & (1 << N)) ? 0 : Z_FLAG; } else { f |= ZSPHTable[reg & (1 << N)]; f |= getF() & C_FLAG; f |= reg & (X_FLAG | Y_FLAG); } setF(f); return T::CC_BIT_R; } template template inline int CPUCore::bit_N_xhl() { byte m = RDMEM(getHL(), T::CC_BIT_XHL_1) & (1 << N); byte f = 0; // N_FLAG if (T::isR800()) { f |= getF() & (S_FLAG | V_FLAG | C_FLAG | X_FLAG | Y_FLAG); f |= H_FLAG; f |= m ? 0 : Z_FLAG; } else { f |= ZSPHTable[m]; f |= getF() & C_FLAG; f |= (T::getMemPtr() >> 8) & (X_FLAG | Y_FLAG); } setF(f); return T::CC_BIT_XHL; } template template inline int CPUCore::bit_N_xix(unsigned addr) { T::setMemPtr(addr); byte m = RDMEM(addr, T::CC_DD + T::CC_BIT_XIX_1) & (1 << N); byte f = 0; // N_FLAG if (T::isR800()) { f |= getF() & (S_FLAG | V_FLAG | C_FLAG | X_FLAG | Y_FLAG); f |= H_FLAG; f |= m ? 0 : Z_FLAG; } else { f |= ZSPHTable[m]; f |= getF() & C_FLAG; f |= (addr >> 8) & (X_FLAG | Y_FLAG); } setF(f); return T::CC_DD + T::CC_BIT_XIX; } // RES n,r static inline byte RES(unsigned b, byte reg) { return reg & ~(1 << b); } template template int CPUCore::res_N_R() { set8(RES(N, get8())); return T::CC_SET_R; } template template inline byte CPUCore::RES_X(unsigned bit, unsigned addr) { byte res = RES(bit, RDMEM(addr, T::CC_SET_XHL_1 + EE)); WRMEM(addr, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::res_N_xhl() { RES_X<0>(N, getHL()); return T::CC_SET_XHL; } template template int CPUCore::res_N_xix_R(unsigned a) { T::setMemPtr(a); set8(RES_X(N, a)); return T::CC_DD + T::CC_SET_XIX; } // SET n,r static inline byte SET(unsigned b, byte reg) { return reg | (1 << b); } template template int CPUCore::set_N_R() { set8(SET(N, get8())); return T::CC_SET_R; } template template inline byte CPUCore::SET_X(unsigned bit, unsigned addr) { byte res = SET(bit, RDMEM(addr, T::CC_SET_XHL_1 + EE)); WRMEM(addr, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::set_N_xhl() { SET_X<0>(N, getHL()); return T::CC_SET_XHL; } template template int CPUCore::set_N_xix_R(unsigned a) { T::setMemPtr(a); set8(SET_X(N, a)); return T::CC_DD + T::CC_SET_XIX; } // RL r template inline byte CPUCore::RL(byte reg) { byte c = reg >> 7; reg = (reg << 1) | ((getF() & C_FLAG) ? 0x01 : 0); byte f = c ? C_FLAG : 0; if (T::isR800()) { f |= ZSPTable[reg]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[reg]; } setF(f); return reg; } template template inline byte CPUCore::RL_X(unsigned x) { byte res = RL(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::rl_R() { set8(RL(get8())); return T::CC_SET_R; } template int CPUCore::rl_xhl() { RL_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::rl_xix_R(unsigned a) { T::setMemPtr(a); set8(RL_X(a)); return T::CC_DD + T::CC_SET_XIX; } // RLC r template inline byte CPUCore::RLC(byte reg) { byte c = reg >> 7; reg = (reg << 1) | c; byte f = c ? C_FLAG : 0; if (T::isR800()) { f |= ZSPTable[reg]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[reg]; } setF(f); return reg; } template template inline byte CPUCore::RLC_X(unsigned x) { byte res = RLC(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::rlc_R() { set8(RLC(get8())); return T::CC_SET_R; } template int CPUCore::rlc_xhl() { RLC_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::rlc_xix_R(unsigned a) { T::setMemPtr(a); set8(RLC_X(a)); return T::CC_DD + T::CC_SET_XIX; } // RR r template inline byte CPUCore::RR(byte reg) { byte c = reg & 1; reg = (reg >> 1) | ((getF() & C_FLAG) << 7); byte f = c ? C_FLAG : 0; if (T::isR800()) { f |= ZSPTable[reg]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[reg]; } setF(f); return reg; } template template inline byte CPUCore::RR_X(unsigned x) { byte res = RR(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::rr_R() { set8(RR(get8())); return T::CC_SET_R; } template int CPUCore::rr_xhl() { RR_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::rr_xix_R(unsigned a) { T::setMemPtr(a); set8(RR_X(a)); return T::CC_DD + T::CC_SET_XIX; } // RRC r template inline byte CPUCore::RRC(byte reg) { byte c = reg & 1; reg = (reg >> 1) | (c << 7); byte f = c ? C_FLAG : 0; if (T::isR800()) { f |= ZSPTable[reg]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[reg]; } setF(f); return reg; } template template inline byte CPUCore::RRC_X(unsigned x) { byte res = RRC(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::rrc_R() { set8(RRC(get8())); return T::CC_SET_R; } template int CPUCore::rrc_xhl() { RRC_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::rrc_xix_R(unsigned a) { T::setMemPtr(a); set8(RRC_X(a)); return T::CC_DD + T::CC_SET_XIX; } // SLA r template inline byte CPUCore::SLA(byte reg) { byte c = reg >> 7; reg <<= 1; byte f = c ? C_FLAG : 0; if (T::isR800()) { f |= ZSPTable[reg]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[reg]; } setF(f); return reg; } template template inline byte CPUCore::SLA_X(unsigned x) { byte res = SLA(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::sla_R() { set8(SLA(get8())); return T::CC_SET_R; } template int CPUCore::sla_xhl() { SLA_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::sla_xix_R(unsigned a) { T::setMemPtr(a); set8(SLA_X(a)); return T::CC_DD + T::CC_SET_XIX; } // SLL r template inline byte CPUCore::SLL(byte reg) { assert(!T::isR800()); // this instruction is Z80-only byte c = reg >> 7; reg = (reg << 1) | 1; byte f = c ? C_FLAG : 0; f |= ZSPXYTable[reg]; setF(f); return reg; } template template inline byte CPUCore::SLL_X(unsigned x) { byte res = SLL(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::sll_R() { set8(SLL(get8())); return T::CC_SET_R; } template int CPUCore::sll_xhl() { SLL_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::sll_xix_R(unsigned a) { T::setMemPtr(a); set8(SLL_X(a)); return T::CC_DD + T::CC_SET_XIX; } template int CPUCore::sll2() { assert(T::isR800()); // this instruction is R800-only byte f = (getF() & (X_FLAG | Y_FLAG)) | (getA() >> 7) | // C_FLAG 0; // all other flags zero setF(f); return T::CC_DD + T::CC_SET_XIX; // TODO } // SRA r template inline byte CPUCore::SRA(byte reg) { byte c = reg & 1; reg = (reg >> 1) | (reg & 0x80); byte f = c ? C_FLAG : 0; if (T::isR800()) { f |= ZSPTable[reg]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[reg]; } setF(f); return reg; } template template inline byte CPUCore::SRA_X(unsigned x) { byte res = SRA(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::sra_R() { set8(SRA(get8())); return T::CC_SET_R; } template int CPUCore::sra_xhl() { SRA_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::sra_xix_R(unsigned a) { T::setMemPtr(a); set8(SRA_X(a)); return T::CC_DD + T::CC_SET_XIX; } // SRL R template inline byte CPUCore::SRL(byte reg) { byte c = reg & 1; reg >>= 1; byte f = c ? C_FLAG : 0; if (T::isR800()) { f |= ZSPTable[reg]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSPXYTable[reg]; } setF(f); return reg; } template template inline byte CPUCore::SRL_X(unsigned x) { byte res = SRL(RDMEM(x, T::CC_SET_XHL_1 + EE)); WRMEM(x, res, T::CC_SET_XHL_2 + EE); return res; } template template int CPUCore::srl_R() { set8(SRL(get8())); return T::CC_SET_R; } template int CPUCore::srl_xhl() { SRL_X<0>(getHL()); return T::CC_SET_XHL; } template template int CPUCore::srl_xix_R(unsigned a) { T::setMemPtr(a); set8(SRL_X(a)); return T::CC_DD + T::CC_SET_XIX; } // RLA RLCA RRA RRCA template int CPUCore::rla() { byte c = getF() & C_FLAG; byte f = (getA() & 0x80) ? C_FLAG : 0; if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG); } else { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG); } setA((getA() << 1) | (c ? 1 : 0)); if (!T::isR800()) { f |= getA() & (X_FLAG | Y_FLAG); } setF(f); return T::CC_RLA; } template int CPUCore::rlca() { setA((getA() << 1) | (getA() >> 7)); byte f = 0; if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG); f |= getA() & C_FLAG; } else { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG); f |= getA() & (Y_FLAG | X_FLAG | C_FLAG); } setF(f); return T::CC_RLA; } template int CPUCore::rra() { byte c = (getF() & C_FLAG) << 7; byte f = (getA() & 0x01) ? C_FLAG : 0; if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG); } else { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG); } setA((getA() >> 1) | c); if (!T::isR800()) { f |= getA() & (X_FLAG | Y_FLAG); } setF(f); return T::CC_RLA; } template int CPUCore::rrca() { byte f = getA() & C_FLAG; if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG); } else { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG); } setA((getA() >> 1) | (getA() << 7)); if (!T::isR800()) { f |= getA() & (X_FLAG | Y_FLAG); } setF(f); return T::CC_RLA; } // RLD template int CPUCore::rld() { byte val = RDMEM(getHL(), T::CC_RLD_1); T::setMemPtr(getHL() + 1); WRMEM(getHL(), (val << 4) | (getA() & 0x0F), T::CC_RLD_2); setA((getA() & 0xF0) | (val >> 4)); byte f = 0; if (T::isR800()) { f |= getF() & (C_FLAG | X_FLAG | Y_FLAG); f |= ZSPTable[getA()]; } else { f |= getF() & C_FLAG; f |= ZSPXYTable[getA()]; } setF(f); return T::CC_RLD; } // RRD template int CPUCore::rrd() { byte val = RDMEM(getHL(), T::CC_RLD_1); T::setMemPtr(getHL() + 1); WRMEM(getHL(), (val >> 4) | (getA() << 4), T::CC_RLD_2); setA((getA() & 0xF0) | (val & 0x0F)); byte f = 0; if (T::isR800()) { f |= getF() & (C_FLAG | X_FLAG | Y_FLAG); f |= ZSPTable[getA()]; } else { f |= getF() & C_FLAG; f |= ZSPXYTable[getA()]; } setF(f); return T::CC_RLD; } // PUSH ss template template inline void CPUCore::PUSH(unsigned reg) { setSP(getSP() - 2); WR_WORD_rev(getSP(), reg, T::CC_PUSH_1 + EE); } template template int CPUCore::push_SS() { PUSH(get16()); return T::CC_PUSH + EE; } // POP ss template template inline unsigned CPUCore::POP() { unsigned addr = getSP(); setSP(addr + 2); return RD_WORD(addr, T::CC_POP_1 + EE); } template template int CPUCore::pop_SS() { set16(POP()); return T::CC_POP + EE; } // CALL nn / CALL cc,nn template template int CPUCore::call(COND cond) { unsigned addr = RD_WORD_PC(T::CC_CALL_1); T::setMemPtr(addr); if (cond(getF())) { PUSH(getPC()); setPC(addr); return T::CC_CALL_A; } else { return T::CC_CALL_B; } } // RST n template template int CPUCore::rst() { PUSH<0>(getPC()); T::setMemPtr(ADDR); setPC(ADDR); return T::CC_RST; } // RET template template inline int CPUCore::RET(COND cond) { if (cond(getF())) { unsigned addr = POP(); T::setMemPtr(addr); setPC(addr); return T::CC_RET_A + EE; } else { return T::CC_RET_B + EE; } } template template int CPUCore::ret(COND cond) { return RET(cond); } template int CPUCore::ret() { return RET<0>(CondTrue()); } template int CPUCore::retn() { // also reti setIFF1(getIFF2()); setSlowInstructions(); return RET(CondTrue()); } // JP ss template template int CPUCore::jp_SS() { setPC(get16()); T::R800ForcePageBreak(); return T::CC_JP_HL + EE; } // JP nn / JP cc,nn template template int CPUCore::jp(COND cond) { unsigned addr = RD_WORD_PC(T::CC_JP_1); T::setMemPtr(addr); if (cond(getF())) { setPC(addr); T::R800ForcePageBreak(); return T::CC_JP_A; } else { return T::CC_JP_B; } } // JR e template template int CPUCore::jr(COND cond) { offset ofst = RDMEM_OPCODE(T::CC_JR_1); if (cond(getF())) { if ((getPC() & 0xFF) == 0) { // On R800, when this instruction is located in the // last two byte of a page (a page is a 256-byte // (aligned) memory block) and even if we jump back, // thus fetching the next opcode byte does not cause a // page-break, there still is one cycle overhead. It's // as-if there is a page-break. // // This could be explained by some (very limited) // pipeline behaviour in R800: it seems that the // decision to cause a page-break on the next // instruction is already made before the jump // destination address for the current instruction is // calculated (though a destination address in another // page is also a reason for a page-break). // // It's likely all instructions behave like this, but I // think we can get away with only explicitly emulating // this behaviour in the djnz and the jr (conditional // or not) instructions: all other instructions that // cause the PC to change in a non-incremental way do // already force a pagebreak for another reason, so // this effect is masked. Examples of such instructions // are: JP, RET, CALL, RST, all repeated block // instructions, accepting an IRQ, (are there more // instructions are events that change PC?) // // See doc/r800-djnz.txt for more details. T::R800ForcePageBreak(); } setPC((getPC() + ofst) & 0xFFFF); T::setMemPtr(getPC()); return T::CC_JR_A; } else { return T::CC_JR_B; } } // DJNZ e template int CPUCore::djnz() { byte b = getB() - 1; setB(b); offset ofst = RDMEM_OPCODE(T::CC_JR_1 + T::EE_DJNZ); if (b) { if ((getPC() & 0xFF) == 0) { // See comment in jr() T::R800ForcePageBreak(); } setPC((getPC() + ofst) & 0xFFFF); T::setMemPtr(getPC()); return T::CC_JR_A + T::EE_DJNZ; } else { return T::CC_JR_B + T::EE_DJNZ; } } // EX (SP),ss template template int CPUCore::ex_xsp_SS() { unsigned res = RD_WORD_impl(getSP(), T::CC_EX_SP_HL_1 + EE); T::setMemPtr(res); WR_WORD_rev(getSP(), get16(), T::CC_EX_SP_HL_2 + EE); set16(res); return T::CC_EX_SP_HL + EE; } // IN r,(c) template template int CPUCore::in_R_c() { if (T::isR800()) T::waitForEvenCycle(T::CC_IN_R_C_1); T::setMemPtr(getBC() + 1); byte res = READ_PORT(getBC(), T::CC_IN_R_C_1); byte f = 0; if (T::isR800()) { f |= getF() & (C_FLAG | X_FLAG | Y_FLAG); f |= ZSPTable[res]; } else { f |= getF() & C_FLAG; f |= ZSPXYTable[res]; } setF(f); set8(res); return T::CC_IN_R_C; } // IN a,(n) template int CPUCore::in_a_byte() { unsigned y = RDMEM_OPCODE(T::CC_IN_A_N_1) + 256 * getA(); T::setMemPtr(y + 1); if (T::isR800()) T::waitForEvenCycle(T::CC_IN_A_N_2); setA(READ_PORT(y, T::CC_IN_A_N_2)); return T::CC_IN_A_N; } // OUT (c),r template template int CPUCore::out_c_R() { if (T::isR800()) T::waitForEvenCycle(T::CC_OUT_C_R_1); T::setMemPtr(getBC() + 1); WRITE_PORT(getBC(), get8(), T::CC_OUT_C_R_1); return T::CC_OUT_C_R; } template int CPUCore::out_c_0() { // TODO not on R800 if (T::isR800()) T::waitForEvenCycle(T::CC_OUT_C_R_1); T::setMemPtr(getBC() + 1); byte out_c_x = isTurboR ? 255 : 0; WRITE_PORT(getBC(), out_c_x, T::CC_OUT_C_R_1); return T::CC_OUT_C_R; } // OUT (n),a template int CPUCore::out_byte_a() { byte port = RDMEM_OPCODE(T::CC_OUT_N_A_1); unsigned y = (getA() << 8) | port; T::setMemPtr((getA() << 8) | ((port + 1) & 255)); if (T::isR800()) T::waitForEvenCycle(T::CC_OUT_N_A_2); WRITE_PORT(y, getA(), T::CC_OUT_N_A_2); return T::CC_OUT_N_A; } // block CP template inline int CPUCore::BLOCK_CP(int increase, bool repeat) { T::setMemPtr(T::getMemPtr() + increase); byte val = RDMEM(getHL(), T::CC_CPI_1); byte res = getA() - val; setHL(getHL() + increase); setBC(getBC() - 1); byte f = ((getA() ^ val ^ res) & H_FLAG) | ZSTable[res] | N_FLAG | (getBC() ? V_FLAG : 0); if (T::isR800()) { f |= getF() & (C_FLAG | X_FLAG | Y_FLAG); } else { f |= getF() & C_FLAG; unsigned k = res - ((f & H_FLAG) >> 4); f |= (k << 4) & Y_FLAG; // bit 1 -> flag 5 f |= k & X_FLAG; // bit 3 -> flag 3 } setF(f); if (repeat && getBC() && res) { setPC(getPC() - 2); T::setMemPtr(getPC() + 1); return T::CC_CPIR; } else { return T::CC_CPI; } } template int CPUCore::cpd() { return BLOCK_CP(-1, false); } template int CPUCore::cpi() { return BLOCK_CP( 1, false); } template int CPUCore::cpdr() { return BLOCK_CP(-1, true ); } template int CPUCore::cpir() { return BLOCK_CP( 1, true ); } // block LD template inline int CPUCore::BLOCK_LD(int increase, bool repeat) { byte val = RDMEM(getHL(), T::CC_LDI_1); WRMEM(getDE(), val, T::CC_LDI_2); setHL(getHL() + increase); setDE(getDE() + increase); setBC(getBC() - 1); byte f = getBC() ? V_FLAG : 0; if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | C_FLAG | X_FLAG | Y_FLAG); } else { f |= getF() & (S_FLAG | Z_FLAG | C_FLAG); f |= ((getA() + val) << 4) & Y_FLAG; // bit 1 -> flag 5 f |= (getA() + val) & X_FLAG; // bit 3 -> flag 3 } setF(f); if (repeat && getBC()) { setPC(getPC() - 2); T::setMemPtr(getPC() + 1); return T::CC_LDIR; } else { return T::CC_LDI; } } template int CPUCore::ldd() { return BLOCK_LD(-1, false); } template int CPUCore::ldi() { return BLOCK_LD( 1, false); } template int CPUCore::lddr() { return BLOCK_LD(-1, true ); } template int CPUCore::ldir() { return BLOCK_LD( 1, true ); } // block IN template inline int CPUCore::BLOCK_IN(int increase, bool repeat) { // TODO R800 flags if (T::isR800()) T::waitForEvenCycle(T::CC_INI_1); T::setMemPtr(getBC() + increase); setBC(getBC() - 0x100); // decr before use byte val = READ_PORT(getBC(), T::CC_INI_1); WRMEM(getHL(), val, T::CC_INI_2); setHL(getHL() + increase); unsigned k = val + ((getC() + increase) & 0xFF); byte b = getB(); setF(((val & S_FLAG) >> 6) | // N_FLAG ((k & 0x100) ? (H_FLAG | C_FLAG) : 0) | ZSXYTable[b] | (ZSPXYTable[(k & 0x07) ^ b] & P_FLAG)); if (repeat && b) { setPC(getPC() - 2); return T::CC_INIR; } else { return T::CC_INI; } } template int CPUCore::ind() { return BLOCK_IN(-1, false); } template int CPUCore::ini() { return BLOCK_IN( 1, false); } template int CPUCore::indr() { return BLOCK_IN(-1, true ); } template int CPUCore::inir() { return BLOCK_IN( 1, true ); } // block OUT template inline int CPUCore::BLOCK_OUT(int increase, bool repeat) { // TODO R800 flags byte val = RDMEM(getHL(), T::CC_OUTI_1); setHL(getHL() + increase); if (T::isR800()) T::waitForEvenCycle(T::CC_OUTI_2); WRITE_PORT(getBC(), val, T::CC_OUTI_2); setBC(getBC() - 0x100); // decr after use T::setMemPtr(getBC() + increase); unsigned k = val + getL(); byte b = getB(); setF(((val & S_FLAG) >> 6) | // N_FLAG ((k & 0x100) ? (H_FLAG | C_FLAG) : 0) | ZSXYTable[b] | (ZSPXYTable[(k & 0x07) ^ b] & P_FLAG)); if (repeat && b) { setPC(getPC() - 2); return T::CC_OTIR; } else { return T::CC_OUTI; } } template int CPUCore::outd() { return BLOCK_OUT(-1, false); } template int CPUCore::outi() { return BLOCK_OUT( 1, false); } template int CPUCore::otdr() { return BLOCK_OUT(-1, true ); } template int CPUCore::otir() { return BLOCK_OUT( 1, true ); } // various template int CPUCore::nop() { return T::CC_NOP; } template int CPUCore::ccf() { byte f = 0; if (T::isR800()) { // H flag is different from Z80 (and as always XY flags as well) f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG | X_FLAG | Y_FLAG | H_FLAG); } else { f |= (getF() & C_FLAG) << 4; // H_FLAG // only set X(Y) flag (don't reset if already set) if (isTurboR) { // Y flag is not changed on a turboR-Z80 f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG | Y_FLAG); f |= (getF() | getA()) & X_FLAG; } else { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG); f |= (getF() | getA()) & (X_FLAG | Y_FLAG); } } f ^= C_FLAG; setF(f); return T::CC_CCF; } template int CPUCore::cpl() { setA(getA() ^ 0xFF); byte f = H_FLAG | N_FLAG; if (T::isR800()) { f |= getF(); } else { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | C_FLAG); f |= getA() & (X_FLAG | Y_FLAG); } setF(f); return T::CC_CPL; } template int CPUCore::daa() { byte a = getA(); byte f = getF(); byte adjust = 0; if ((f & H_FLAG) || ((getA() & 0xf) > 9)) adjust += 6; if ((f & C_FLAG) || (getA() > 0x99)) adjust += 0x60; if (f & N_FLAG) a -= adjust; else a += adjust; if (T::isR800()) { f &= C_FLAG | N_FLAG | X_FLAG | Y_FLAG; f |= ZSPTable[a]; } else { f &= C_FLAG | N_FLAG; f |= ZSPXYTable[a]; } f |= (getA() > 0x99) | ((getA() ^ a) & H_FLAG); setA(a); setF(f); return T::CC_DAA; } template int CPUCore::neg() { // alternative: LUT word negTable[256] unsigned a = getA(); unsigned res = -signed(a); byte f = ((res & 0x100) ? C_FLAG : 0) | N_FLAG | ((res ^ a) & H_FLAG) | ((a & res & 0x80) >> 5); // V_FLAG if (T::isR800()) { f |= ZSTable[res & 0xFF]; f |= getF() & (X_FLAG | Y_FLAG); } else { f |= ZSXYTable[res & 0xFF]; } setF(f); setA(res); return T::CC_NEG; } template int CPUCore::scf() { byte f = C_FLAG; if (T::isR800()) { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | X_FLAG | Y_FLAG); } else { // only set X(Y) flag (don't reset if already set) if (isTurboR) { // Y flag is not changed on a turboR-Z80 f |= getF() & (S_FLAG | Z_FLAG | P_FLAG | Y_FLAG); f |= (getF() | getA()) & X_FLAG; } else { f |= getF() & (S_FLAG | Z_FLAG | P_FLAG); f |= (getF() | getA()) & (X_FLAG | Y_FLAG); } } setF(f); return T::CC_SCF; } template int CPUCore::ex_af_af() { unsigned t = getAF2(); setAF2(getAF()); setAF(t); return T::CC_EX; } template int CPUCore::ex_de_hl() { unsigned t = getDE(); setDE(getHL()); setHL(t); return T::CC_EX; } template int CPUCore::exx() { unsigned t1 = getBC2(); setBC2(getBC()); setBC(t1); unsigned t2 = getDE2(); setDE2(getDE()); setDE(t2); unsigned t3 = getHL2(); setHL2(getHL()); setHL(t3); return T::CC_EX; } template int CPUCore::di() { setIFF1(false); setIFF2(false); return T::CC_DI; } template int CPUCore::ei() { setIFF1(true); setIFF2(true); setAfterEI(); // no ints directly after this instr setSlowInstructions(); return T::CC_EI; } template int CPUCore::halt() { setHALT(true); setSlowInstructions(); if (!(getIFF1() || getIFF2())) { diHaltCallback.execute(); } return T::CC_HALT; } template template int CPUCore::im_N() { setIM(N); return T::CC_IM; } // LD A,I/R template template int CPUCore::ld_a_IR() { setA(get8()); byte f = getIFF2() ? V_FLAG : 0; if (T::isR800()) { f |= getF() & (C_FLAG | X_FLAG | Y_FLAG); f |= ZSTable[getA()]; } else { f |= getF() & C_FLAG; f |= ZSXYTable[getA()]; // see comment in the IRQ acceptance part of executeSlow(). setAfterLDAI(); // only Z80 (not R800) has this quirk setSlowInstructions(); } setF(f); return T::CC_LD_A_I; } // LD I/R,A template int CPUCore::ld_r_a() { // This code sequence: // XOR A / LD R,A / LD A,R // gives A=2 for Z80, but A=1 for R800. The difference can possibly be // explained by a difference in the relative time between writing the // new value to the R register and increasing the R register per M1 // cycle. Here we implemented the R800 behaviour by storing 'A-1' into // R, that's good enough for now. byte val = getA(); if (T::isR800()) val -= 1; setR(val); return T::CC_LD_A_I; } template int CPUCore::ld_i_a() { setI(getA()); return T::CC_LD_A_I; } // MULUB A,r template template int CPUCore::mulub_a_R() { assert(T::isR800()); // this instruction is R800-only // Verified on real R800: // YHXN flags are unchanged // SV flags are reset // Z flag is set when result is zero // C flag is set when result doesn't fit in 8-bit setHL(unsigned(getA()) * get8()); setF((getF() & (N_FLAG | H_FLAG | X_FLAG | Y_FLAG)) | 0 | // S_FLAG V_FLAG (getHL() ? 0 : Z_FLAG) | ((getHL() & 0xFF00) ? C_FLAG : 0)); return T::CC_MULUB; } // MULUW HL,ss template template int CPUCore::muluw_hl_SS() { assert(T::isR800()); // this instruction is R800-only // Verified on real R800: // YHXN flags are unchanged // SV flags are reset // Z flag is set when result is zero // C flag is set when result doesn't fit in 16-bit unsigned res = unsigned(getHL()) * get16(); setDE(res >> 16); setHL(res & 0xffff); setF((getF() & (N_FLAG | H_FLAG | X_FLAG | Y_FLAG)) | 0 | // S_FLAG V_FLAG (res ? 0 : Z_FLAG) | ((res & 0xFFFF0000) ? C_FLAG : 0)); return T::CC_MULUW; } // versions: // 1 -> initial version // 2 -> moved memptr from here to Z80TYPE (and not to R800TYPE) // 3 -> timing of the emulation changed (no changes in serialization) template template void CPUCore::serialize(Archive& ar, unsigned version) { T::serialize(ar, version); ar.serialize("regs", static_cast(*this)); if (ar.versionBelow(version, 2)) { unsigned memptr = 0; // dummy value (avoid warning) ar.serialize("memptr", memptr); T::setMemPtr(memptr); } if (ar.isLoader()) { invalidateMemCache(0x0000, 0x10000); } // don't serialize // IRQStatus // NMIStatus, nmiEdge // slowInstructions // exitLoop if (T::isR800() && ar.versionBelow(version, 3)) { motherboard.getMSXCliComm().printWarning( "Loading an old savestate: the timing of the R800 " "emulation has changed. This may cause synchronization " "problems in replay."); } } // Force template instantiation template class CPUCore; template class CPUCore; INSTANTIATE_SERIALIZE_METHODS(CPUCore); INSTANTIATE_SERIALIZE_METHODS(CPUCore); } // namespace openmsx openmsx-0.10.0/src/cpu/CPUClock.hh0000644000175000017500000000756312262345041017361 0ustar manuelmanuel00000000000000#ifndef CPUCLOCK_HH #define CPUCLOCK_HH #include "DynamicClock.hh" #include "Scheduler.hh" #include "likely.hh" #include #include namespace openmsx { class Scheduler; class CPUClock { public: unsigned getFreq() const { return clock.getFreq(); } protected: CPUClock(EmuTime::param time, Scheduler& scheduler); // benchmarking showed a slowdown of ~3% on AMD64 // when using the following code: #if 0 // 64-bit addition is cheap inline void add(unsigned ticks) { clock += ticks; } inline void sync() const { } #else // 64-bit addition is expensive // (if executed several million times per second) inline void add(unsigned ticks) { remaining -= ticks; } inline void sync() const { clock.fastAdd(limit - remaining); limit = remaining; } #endif // These are similar to the corresponding methods in DynamicClock. EmuTime::param getTime() const { sync(); return clock.getTime(); } const EmuTime getTimeFast() const { return clock.getFastAdd(limit - remaining); } const EmuTime getTimeFast(int cc) const { return clock.getFastAdd(limit - remaining + cc); } void setTime(EmuTime::param time) { sync(); clock.reset(time); } void setFreq(unsigned freq) { clock.setFreq(freq); } void advanceTime(EmuTime::param time); /** Implementation of the HALT instruction timing. * Advances the clock with an integer multiple of 'hltStates' cycles * so that it equal or bigger than 'time'. Returns the number of times * 'hltStates' needed to be added. */ unsigned advanceHalt(unsigned hltStates, EmuTime::param time) { sync(); unsigned ticks = clock.getTicksTillUp(time); unsigned halts = (ticks + hltStates - 1) / hltStates; // round up clock += halts * hltStates; return halts; } /** R800 runs at 7MHz, but I/O is done over a slower 3.5MHz bus. So * sometimes right before I/O it's needed to wait for one cycle so * that we're at the start of a clock cycle of the slower bus. */ void waitForEvenCycle(int cc) { sync(); auto totalTicks = clock.getTotalTicks() + cc; if (totalTicks & 1) { add(1); } } // The following 3 methods are used in the innermost CPU loop. It // allows to implement this loop with just one test. This loop must // be exited on the following three conditions: // 1) a Synchronization Point is reached // 2) a 'slow' instruction must be executed (ei, di, ..) // 3) another thread has requested to exit the loop // The limitReached() method indicates whether the loop should be // exited. // The earliest SP must always be kept up-to-date with the setLimit() // method, so after every action that might change SP. The Scheduler // class is responsible for this. // In 'slow' mode the limit mechanism can be disabled with the // disableLimit() method. In disabled mode, the limitReached() method // always returns true. // When another thread requests to exit the loop, it's not needed to // already exit at the next instruction. If we exit soon that's good // enough. This is implemented by simply regularly exiting the loop // (outside the inner loop, the real exit condition should be tested). void setLimit(EmuTime::param time) { if (limitEnabled) { sync(); assert(remaining == limit); int newLimit = std::min(15000u, clock.getTicksTillUp(time) - 1); if (limit < 0) { limit = newLimit; } else { limit = std::min(limit, newLimit); } remaining = limit; } else { assert(limit < 0); } } void enableLimit() { limitEnabled = true; setLimit(scheduler.getNext()); } void disableLimit() { limitEnabled = false; int extra = limit - remaining; limit = -1; remaining = limit - extra; } inline bool limitReached() const { return remaining < 0; } template void serialize(Archive& ar, unsigned version); private: mutable DynamicClock clock; Scheduler& scheduler; int remaining; mutable int limit; bool limitEnabled; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/node.mk0000644000175000017500000000047512262345041016706 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ CPURegs CPUCore CPUClock Dasm \ BreakPointBase BreakPoint WatchPoint DebugCondition \ MSXCPUInterface MSXCPU \ MSXMultiDevice MSXMultiIODevice MSXMultiMemDevice \ MSXWatchIODevice \ VDPIODelay \ IRQHelper HDR_ONLY:= \ Z80 R800 \ CacheLine include build/node-end.mk openmsx-0.10.0/src/cpu/BreakPoint.cc0000644000175000017500000000070412262345041017770 0ustar manuelmanuel00000000000000#include "BreakPoint.hh" #include "TclObject.hh" namespace openmsx { unsigned BreakPoint::lastId = 0; BreakPoint::BreakPoint(GlobalCliComm& cliComm, word address_, TclObject command, TclObject condition) : BreakPointBase(cliComm, command, condition) , id(++lastId) , address(address_) { } word BreakPoint::getAddress() const { return address; } unsigned BreakPoint::getId() const { return id; } } // namespace openmsx openmsx-0.10.0/src/cpu/MSXCPUInterface.hh0000644000175000017500000002443712262345041020615 0ustar manuelmanuel00000000000000#ifndef MSXCPUINTERFACE_HH #define MSXCPUINTERFACE_HH #include "CacheLine.hh" #include "MSXDevice.hh" #include "WatchPoint.hh" #include "openmsx.hh" #include "noncopyable.hh" #include "likely.hh" #include #include #include namespace openmsx { class VDPIODelay; class DummyDevice; class MSXMotherBoard; class MSXCPU; class CliComm; class MemoryDebug; class SlottedMemoryDebug; class IODebug; class SlotInfo; class SubSlottedInfo; class ExternalSlotInfo; class IOInfo; class BreakPoint; class DebugCondition; class MSXCPUInterface : private noncopyable { public: explicit MSXCPUInterface(MSXMotherBoard& motherBoard); ~MSXCPUInterface(); /** * Devices can register their In ports. This is normally done * in their constructor. Once device are registered, their * readIO() method can get called. */ void register_IO_In(byte port, MSXDevice* device); void unregister_IO_In(byte port, MSXDevice* device); /** * Devices can register their Out ports. This is normally done * in their constructor. Once device are registered, their * writeIO() method can get called. */ void register_IO_Out(byte port, MSXDevice* device); void unregister_IO_Out(byte port, MSXDevice* device); /** * Devices can register themself in the MSX slotstructure. * This is normally done in their constructor. Once devices * are registered their readMem() / writeMem() methods can * get called. */ void registerMemDevice(MSXDevice& device, int primSl, int secSL, int base, int size); void unregisterMemDevice(MSXDevice& device, int primSl, int secSL, int base, int size); /** (Un)register global writes. * @see MSXDevice::globalWrite() */ void registerGlobalWrite(MSXDevice& device, word address); void unregisterGlobalWrite(MSXDevice& device, word address); /** * Reset (the slot state) */ void reset(); /** * This reads a byte from the currently selected device */ inline byte readMem(word address, EmuTime::param time) { if (unlikely(disallowReadCache[address >> CacheLine::BITS])) { return readMemSlow(address, time); } return visibleDevices[address >> 14]->readMem(address, time); } /** * This writes a byte to the currently selected device */ inline void writeMem(word address, byte value, EmuTime::param time) { if (unlikely(disallowWriteCache[address >> CacheLine::BITS])) { writeMemSlow(address, value, time); } visibleDevices[address>>14]->writeMem(address, value, time); } /** * This read a byte from the given IO-port * @see MSXDevice::readIO() */ inline byte readIO(word port, EmuTime::param time) { return IO_In[port & 0xFF]->readIO(port, time); } /** * This writes a byte to the given IO-port * @see MSXDevice::writeIO() */ inline void writeIO(word port, byte value, EmuTime::param time) { IO_Out[port & 0xFF]->writeIO(port, value, time); } /** * Test that the memory in the interval [start, start + * CacheLine::SIZE) is cacheable for reading. If it is, a pointer to a * buffer containing this interval must be returned. If not, a null * pointer must be returned. * Cacheable for reading means the data may be read directly * from the buffer, thus bypassing the readMem() method, and * thus also ignoring EmuTime. * The default implementation always returns a null pointer. * An interval will never cross a 16KB border. * An interval will never contain the address 0xffff. */ inline const byte* getReadCacheLine(word start) const { if (unlikely(disallowReadCache[start >> CacheLine::BITS])) { return nullptr; } return visibleDevices[start >> 14]->getReadCacheLine(start); } /** * Test that the memory in the interval [start, start + * CacheLine::SIZE) is cacheable for writing. If it is, a pointer to a * buffer containing this interval must be returned. If not, a null * pointer must be returned. * Cacheable for writing means the data may be written directly * to the buffer, thus bypassing the writeMem() method, and * thus also ignoring EmuTime. * The default implementation always returns a null pointer. * An interval will never cross a 16KB border. * An interval will never contain the address 0xffff. */ inline byte* getWriteCacheLine(word start) const { if (unlikely(disallowWriteCache[start >> CacheLine::BITS])) { return nullptr; } return visibleDevices[start >> 14]->getWriteCacheLine(start); } /** * CPU uses this method to read 'extra' data from the databus * used in interrupt routines. In MSX this returns always 255. */ byte readIRQVector(); /* * Should only be used by PPI * TODO: make private / friend */ void setPrimarySlots(byte value); /** * Peek memory location * @see MSXDevice::peekMem() */ byte peekMem(word address, EmuTime::param time) const; byte peekSlottedMem(unsigned address, EmuTime::param time) const; byte readSlottedMem(unsigned address, EmuTime::param time); void writeSlottedMem(unsigned address, byte value, EmuTime::param time); void setExpanded(int ps); void unsetExpanded(int ps); void testUnsetExpanded(int ps, std::vector allowed) const; inline bool isExpanded(int ps) const { return expanded[ps] != 0; } void changeExpanded(bool isExpanded); DummyDevice& getDummyDevice(); static void insertBreakPoint(const std::shared_ptr& bp); static void removeBreakPoint(const BreakPoint& bp); // note: must be shared_ptr (not unique_ptr), see checkBreakPoints() // TODO use multi_set sorted on BreakPoint->getAddress() typedef std::multimap> BreakPoints; static const BreakPoints& getBreakPoints(); void setWatchPoint(const std::shared_ptr& watchPoint); void removeWatchPoint(std::shared_ptr watchPoint); // note: must be shared_ptr (not unique_ptr), see WatchIO::doReadCallback() typedef std::vector> WatchPoints; const WatchPoints& getWatchPoints() const; static void setCondition(const std::shared_ptr& cond); static void removeCondition(const DebugCondition& cond); // note: must be shared_ptr (not unique_ptr), see checkBreakPoints() typedef std::vector> Conditions; static const Conditions& getConditions(); static bool isBreaked() { return breaked; } void doBreak(); void doStep(); void doContinue(); // should only be used by CPUCore static bool isStep() { return step; } static void setStep (bool x) { step = x; } static bool isContinue() { return continued; } static void setContinue(bool x) { continued = x; } // breakpoint methods used by CPUCore static bool anyBreakPoints() { return !breakPoints.empty() || !conditions.empty(); } static bool checkBreakPoints(unsigned pc) { auto range = breakPoints.equal_range(pc); if (conditions.empty() && (range.first == range.second)) { return false; } // slow path non-inlined checkBreakPoints(range); return isBreaked(); } // cleanup global variables static void cleanup(); // In fast-forward mode, breakpoints, watchpoints and conditions should // not trigger. void setFastForward(bool fastForward_) { fastForward = fastForward_; } bool isFastForward() const { return fastForward; } template void serialize(Archive& ar, unsigned version); private: byte readMemSlow(word address, EmuTime::param time); void writeMemSlow(word address, byte value, EmuTime::param time); MSXDevice*& getDevicePtr(byte port, bool isIn); void register_IO (int port, bool isIn, MSXDevice*& devicePtr, MSXDevice* device); void unregister_IO(MSXDevice*& devicePtr, MSXDevice* device); void testRegisterSlot(MSXDevice& device, int ps, int ss, int base, int size); void registerSlot(MSXDevice& device, int ps, int ss, int base, int size); void unregisterSlot(MSXDevice& device, int ps, int ss, int base, int size); static void checkBreakPoints(std::pair range); void removeAllWatchPoints(); void registerIOWatch (WatchPoint& watchPoint, MSXDevice** devices); void unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices); void updateMemWatch(WatchPoint::Type type); void executeMemWatch(WatchPoint::Type type, unsigned address, unsigned value = ~0u); void doContinue2(); friend class SlotInfo; friend class IODebug; friend class IOInfo; const std::unique_ptr memoryDebug; const std::unique_ptr slottedMemoryDebug; const std::unique_ptr ioDebug; const std::unique_ptr slotInfo; const std::unique_ptr subSlottedInfo; const std::unique_ptr externalSlotInfo; const std::unique_ptr inputPortInfo; const std::unique_ptr outputPortInfo; /** Updated visibleDevices for a given page and clears the cache * on changes. * Should be called whenever PrimarySlotState or SecondarySlotState * was modified. * @param page page [0..3] to update visibleDevices for. */ void updateVisible(int page); inline void updateVisible(int page, int ps, int ss); void setSubSlot(byte primSlot, byte value); std::unique_ptr dummyDevice; MSXCPU& msxcpu; CliComm& cliComm; MSXMotherBoard& motherBoard; std::unique_ptr delayDevice; byte disallowReadCache [CacheLine::NUM]; byte disallowWriteCache[CacheLine::NUM]; std::bitset readWatchSet [CacheLine::NUM]; std::bitset writeWatchSet[CacheLine::NUM]; struct GlobalWriteInfo { MSXDevice* device; word addr; bool operator==(const GlobalWriteInfo& rhs) const { return (device == rhs.device) && (addr == rhs.addr); } }; std::vector globalWrites; MSXDevice* IO_In [256]; MSXDevice* IO_Out[256]; MSXDevice* slotLayout[4][4][4]; MSXDevice* visibleDevices[4]; byte subSlotRegister[4]; byte primarySlotState[4]; byte secondarySlotState[4]; unsigned expanded[4]; bool fastForward; // no need to serialize // All CPUs (Z80 and R800) of all MSX machines share this state. static BreakPoints breakPoints; WatchPoints watchPoints; // TODO must also be static static Conditions conditions; static bool breaked; static bool continued; static bool step; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/MSXMultiIODevice.hh0000644000175000017500000000137312262345041021001 0ustar manuelmanuel00000000000000#ifndef MSXMULTIIODEVICE_HH #define MSXMULTIIODEVICE_HH #include "MSXMultiDevice.hh" #include namespace openmsx { class MSXMultiIODevice : public MSXMultiDevice { public: typedef std::vector Devices; explicit MSXMultiIODevice(const HardwareConfig& hwConf); virtual ~MSXMultiIODevice(); void addDevice(MSXDevice* device); void removeDevice(MSXDevice* device); Devices& getDevices(); // MSXDevice virtual std::string getName() const; virtual void getNameList(TclObject& result) const; virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); private: Devices devices; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/Z80.hh0000644000175000017500000001207312262345041016327 0ustar manuelmanuel00000000000000#ifndef Z80_HH #define Z80_HH #include "CPUClock.hh" #include "inline.hh" #include namespace openmsx { class CPURegs; class Z80TYPE : public CPUClock { protected: template struct Normalize { static const bool value = false; }; static const int CLOCK_FREQ = 3579545; static const int WAIT_CYCLES = 1; Z80TYPE(EmuTime::param time, Scheduler& scheduler) : CPUClock(time, scheduler) { } ALWAYS_INLINE unsigned haltStates() const { return 4 + WAIT_CYCLES; } // HALT + M1 ALWAYS_INLINE bool isR800() const { return false; } template ALWAYS_INLINE void PRE_MEM (unsigned /*address*/) { } template < bool> ALWAYS_INLINE void POST_MEM (unsigned /*address*/) { } template ALWAYS_INLINE void PRE_WORD (unsigned /*address*/) { } template < bool> ALWAYS_INLINE void POST_WORD(unsigned /*address*/) { } ALWAYS_INLINE void R800Refresh(CPURegs& /*R*/) { } ALWAYS_INLINE void R800ForcePageBreak() { } ALWAYS_INLINE void setMemPtr(unsigned x) { memptr = x; } ALWAYS_INLINE unsigned getMemPtr() const { return memptr; } static const int CC_LD_A_SS = 5+3, CC_LD_A_SS_1 = 5+1, CC_LD_A_NN = 5+3+3+3, CC_LD_A_NN_1 = 5+1, CC_LD_A_NN_2 = 5+3+3+1, CC_LD_A_I = 5+6, CC_LD_R_R = 5, CC_LD_R_N = 5+3, CC_LD_R_N_1 = 5+1, CC_LD_R_HL = 5+3, CC_LD_R_HL_1 = 5+1, CC_LD_R_XIX = 5+3+5+3, CC_LD_R_XIX_1 = 5+1, CC_LD_R_XIX_2 = 5+3+5+1, // +5 CC_LD_HL_R = 5+3, CC_LD_HL_R_1 = 5+1, CC_LD_HL_N = 5+3+3, CC_LD_HL_N_1 = 5+1, CC_LD_HL_N_2 = 5+3+1, CC_LD_SS_A = 5+3, CC_LD_SS_A_1 = 5+1, CC_LD_NN_A = 5+3+3+3, CC_LD_NN_A_1 = 5+1, CC_LD_NN_A_2 = 5+3+3+1, CC_LD_XIX_R = 5+3+5+3, CC_LD_XIX_R_1 = 5+1, CC_LD_XIX_R_2 = 5+3+5+1, // +5 CC_LD_XIX_N = 5+3+5+3, CC_LD_XIX_N_1 = 5+1, CC_LD_XIX_N_2 = 5+3+5+1, // +5 CC_LD_HL_XX = 5+3+3+3+3, CC_LD_HL_XX_1 = 5+1, CC_LD_HL_XX_2 = 5+3+3+1, CC_LD_SP_HL = 7, CC_LD_SS_NN = 5+3+3, CC_LD_SS_NN_1 = 5+1, CC_LD_XX_HL = 5+3+3+3+3, CC_LD_XX_HL_1 = 5+1, CC_LD_XX_HL_2 = 5+3+3+1, CC_CP_R = 5, CC_CP_N = 5+3, CC_CP_N_1 = 5+1, CC_CP_XHL = 5+3, CC_CP_XHL_1 = 5+1, CC_CP_XIX = 5+3+5+3, CC_CP_XIX_1 = 5+1, CC_CP_XIX_2 = 5+3+5+1, // +5 CC_INC_R = 5, CC_INC_XHL = 5+4+3, CC_INC_XHL_1 = 5+1, CC_INC_XHL_2 = 5+4+1, CC_INC_XIX = 5+3+5+4+3, CC_INC_XIX_1 = 5+1, EE_INC_XIX = 8, // +5 CC_INC_SS = 7, CC_ADD_HL_SS = 5+4+3, CC_ADC_HL_SS = 5+5+4+3, CC_LDI = 5+5+3+5, CC_LDI_1 = 5+5+1, CC_LDI_2 = 5+5+3+1, CC_LDIR = 5+5+3+5+5, CC_CPI = 5+5+3+5, CC_CPI_1 = 5+5+1, CC_CPIR = 5+5+3+5+5, CC_PUSH = 6+3+3, CC_PUSH_1 = 6+1, CC_POP = 5+3+3, CC_POP_1 = 5+1, CC_CALL = 5+3+4+3+3, CC_CALL_1 = 5+1, EE_CALL = 6, CC_CALL_A = 5+3+4+3+3, CC_CALL_B = 5+3+3, CC_RST = 6+3+3, CC_RET_A = 5+3+3, CC_RET_B = 5, EE_RET_C = 1, CC_RETN = 5+5+3+3, EE_RETN = 5, CC_JP_A = 5+3+3, CC_JP_B = 5+3+3, CC_JP_1 = 5+1, CC_JP_HL = 5, CC_JR_A = 5+3+5, CC_JR_B = 5+3, CC_JR_1 = 5+1, CC_DJNZ = 6+3+5, EE_DJNZ = 1, CC_EX_SP_HL = 5+3+4+3+5, CC_EX_SP_HL_1 = 5+1, CC_EX_SP_HL_2 = 5+3+4+1, CC_BIT_R = 5+5, CC_BIT_XHL = 5+5+4, CC_BIT_XHL_1 = 5+5+1, CC_BIT_XIX = 5+3+5+4, CC_BIT_XIX_1 = 5+3+5+1, // +5 CC_SET_R = 5+5, CC_SET_XHL = 5+5+4+3, CC_SET_XHL_1 = 5+5+1, CC_SET_XHL_2 = 5+5+4+1, CC_SET_XIX = 5+3+5+4+3, EE_SET_XIX = 3, // +5 CC_RLA = 5, CC_RLD = 5+5+3+4+3, CC_RLD_1 = 5+5+1, CC_RLD_2 = 5+5+3+4+1, CC_IN_A_N = 5+3+4, CC_IN_A_N_1 = 5+1, CC_IN_A_N_2 = 5+3+1, CC_IN_R_C = 5+5+4, CC_IN_R_C_1 = 5+5+1, CC_INI = 5+6+4+3, CC_INI_1 = 5+6+1, CC_INI_2 = 5+6+4+1, CC_INIR = 5+6+4+3+5, CC_OUT_N_A = 5+3+4, CC_OUT_N_A_1 = 5+1, CC_OUT_N_A_2 = 5+3+1, CC_OUT_C_R = 5+5+4, CC_OUT_C_R_1 = 5+5+1, CC_OUTI = 5+6+3+4, CC_OUTI_1 = 5+6+1, CC_OUTI_2 = 5+6+3+1, CC_OTIR = 5+6+3+4+5, CC_EX = 5, CC_NOP = 5, CC_CCF = 5, CC_SCF = 5, CC_DAA = 5, CC_NEG = 5+5, CC_CPL = 5, CC_DI = 5, CC_EI = 5, CC_HALT = 5, CC_IM = 5+5, CC_MULUB = 0, CC_MULUW = 0, CC_NMI = 5+3+3, EE_NMI_1 = -1, CC_IRQ0 = 7+3+3, EE_IRQ0_1 = 1, CC_IRQ1 = 7+3+3, EE_IRQ1_1 = 1, CC_IRQ2 = 7+3+3+3+3, EE_IRQ2_1 = 1, CC_IRQ2_2 = 7+3+3+1, CC_MAIN = 1, CC_DD = 5, CC_PREFIX = 5+1, CC_DD_CB = 5+1, // +5 EE_ED = 5, CC_RDMEM = 3, CC_WRMEM = 3; // versions (version number shared with CPUCore<> class) // 1 -> initial version // 2 -> moved memptr from CPUCore to here template void serialize(Archive& ar, unsigned version) { CPUClock::serialize(ar, version); if (version >= 2) { ar.serialize("memptr", memptr); } } private: unsigned memptr; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/CPUClock.cc0000644000175000017500000000104512262345041017334 0ustar manuelmanuel00000000000000#include "CPUClock.hh" #include "serialize.hh" namespace openmsx { CPUClock::CPUClock(EmuTime::param time, Scheduler& scheduler_) : clock(time) , scheduler(scheduler_) , remaining(-1), limit(-1), limitEnabled(false) { } void CPUClock::advanceTime(EmuTime::param time) { remaining = limit; clock.advance(time); setLimit(scheduler.getNext()); } template void CPUClock::serialize(Archive& ar, unsigned /*version*/) { sync(); ar.serialize("clock", clock); } INSTANTIATE_SERIALIZE_METHODS(CPUClock); } // namespace openmsx openmsx-0.10.0/src/cpu/MSXCPU.hh0000644000175000017500000001121512262345041016762 0ustar manuelmanuel00000000000000#ifndef MSXCPU_HH #define MSXCPU_HH #include "CPURegs.hh" #include "Observer.hh" #include "EmuTime.hh" #include "noncopyable.hh" #include "serialize_meta.hh" #include #include namespace openmsx { class MSXMotherBoard; class MSXCPUInterface; class BooleanSetting; class Z80TYPE; class R800TYPE; template class CPUCore; class Setting; class TimeInfoTopic; class CPUFreqInfoTopic; class MSXCPUDebuggable; class TclCallback; class TclObject; class MSXCPU : private Observer, private noncopyable { public: enum CPUType { CPU_Z80, CPU_R800 }; explicit MSXCPU(MSXMotherBoard& motherboard); ~MSXCPU(); /** Reset CPU. * Requires CPU is not in the middle of an instruction, * so exitCPULoop was called and execute() method returned. */ void doReset(EmuTime::param time); /** Switch between Z80/R800. */ void setActiveCPU(CPUType cpu); /** Sets DRAM or ROM mode (influences memory access speed for R800). */ void setDRAMmode(bool dram); /** Inform CPU of bank switch. This will invalidate memory cache and * update memory timings on R800. */ void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot); /** Invalidate the CPU its cache for the interval [start, start + size) * For example MSXMemoryMapper and MSXGameCartrigde need to call this * method when a 'memory switch' occurs. */ void invalidateMemCache(word start, unsigned size); /** This method raises a maskable interrupt. A device may call this * method more than once. If the device wants to lower the * interrupt again it must call the lowerIRQ() method exactly as * many times. * Before using this method take a look at IRQHelper. */ void raiseIRQ(); /** This methods lowers the maskable interrupt again. A device may never * call this method more often than it called the method * raiseIRQ(). * Before using this method take a look at IRQHelper. */ void lowerIRQ(); /** This method raises a non-maskable interrupt. A device may call this * method more than once. If the device wants to lower the * interrupt again it must call the lowerNMI() method exactly as * many times. * Before using this method take a look at IRQHelper. */ void raiseNMI(); /** This methods lowers the non-maskable interrupt again. A device may never * call this method more often than it called the method * raiseNMI(). * Before using this method take a look at IRQHelper. */ void lowerNMI(); /** Should only be used from within a MSXDevice::readMem() method. * Returns true if that read was the first byte of an instruction * (the Z80 M1 pin is active). This implementation is not 100% * accurate, but good enough for now. */ bool isM1Cycle(unsigned address) const; /** See CPUCore::exitCPULoopsync() */ void exitCPULoopSync(); /** See CPUCore::exitCPULoopAsync() */ void exitCPULoopAsync(); /** Is the R800 currently active? */ bool isR800Active() const { return !z80Active; } /** Switch the Z80 clock freq. */ void setZ80Freq(unsigned freq); void setInterface(MSXCPUInterface* interf); void disasmCommand(const std::vector& tokens, TclObject& result) const; /** (un)pause CPU. During pause the CPU executes NOP instructions * continuously (just like during HALT). Used by turbor hw pause. */ void setPaused(bool paused); void setNextSyncPoint(EmuTime::param time); void wait(EmuTime::param time); void waitCycles(unsigned cycles); void waitCyclesR800(unsigned cycles); CPURegs& getRegisters(); template void serialize(Archive& ar, unsigned version); private: // only for MSXMotherBoard void execute(bool fastForward); friend class MSXMotherBoard; /** The time returned by this method is not safe to use for Scheduler * or IO related stuff (because it can sometimes already be higher then * still to be scheduled sync points). Use Scheduler::getCurrentTime() * instead. * TODO is this comment still true? */ EmuTime::param getCurrentTime() const; // Observer virtual void update(const Setting& setting); MSXMotherBoard& motherboard; const std::unique_ptr traceSetting; const std::unique_ptr diHaltCallback; const std::unique_ptr> z80; const std::unique_ptr> r800; friend class TimeInfoTopic; friend class MSXCPUDebuggable; const std::unique_ptr timeInfo; const std::unique_ptr z80FreqInfo; const std::unique_ptr r800FreqInfo; const std::unique_ptr debuggable; EmuTime reference; bool z80Active; bool newZ80Active; }; SERIALIZE_CLASS_VERSION(MSXCPU, 2); } // namespace openmsx #endif openmsx-0.10.0/src/cpu/MSXMultiMemDevice.cc0000644000175000017500000000756212262345041021204 0ustar manuelmanuel00000000000000#include "MSXMultiMemDevice.hh" #include "DummyDevice.hh" #include "MSXCPUInterface.hh" #include "TclObject.hh" #include "StringOp.hh" #include "likely.hh" #include "unreachable.hh" #include "xrange.hh" #include #include namespace openmsx { MSXMultiMemDevice::Range::Range( unsigned base_, unsigned size_, MSXDevice& device_) : base(base_), size(size_), device(&device_) { } bool MSXMultiMemDevice::Range::operator==(const Range& other) const { return (base == other.base) && (size == other.size) && (device == other.device); } MSXMultiMemDevice::MSXMultiMemDevice(const HardwareConfig& hwConf) : MSXMultiDevice(hwConf) { // add sentinel at the end ranges.push_back(Range(0x0000, 0x10000, getCPUInterface().getDummyDevice())); } MSXMultiMemDevice::~MSXMultiMemDevice() { assert(empty()); } static bool isInside(unsigned x, unsigned start, unsigned size) { return (x - start) < size; } static bool overlap(unsigned start1, unsigned size1, unsigned start2, unsigned size2) { return (isInside(start1, start2, size2)) || (isInside(start1 + size1 - 1, start2, size2)); } bool MSXMultiMemDevice::canAdd(int base, int size) { for (auto i : xrange(ranges.size() - 1)) { if (overlap(base, size, ranges[i].base, ranges[i].size)) { return false; } } return true; } void MSXMultiMemDevice::add(MSXDevice& device, int base, int size) { assert(canAdd(base, size)); ranges.insert(ranges.begin(), Range(base, size, device)); } void MSXMultiMemDevice::remove(MSXDevice& device, int base, int size) { auto it = find(ranges.begin(), ranges.end(), Range(base, size, device)); assert(it != ranges.end()); ranges.erase(it); } bool MSXMultiMemDevice::empty() const { return ranges.size() == 1; } std::vector MSXMultiMemDevice::getDevices() const { std::vector result; for (auto i : xrange(ranges.size() - 1)) { result.push_back(ranges[i].device); } return result; } std::string MSXMultiMemDevice::getName() const { TclObject list; getNameList(list); return list.getString().str(); } void MSXMultiMemDevice::getNameList(TclObject& result) const { for (auto& r : ranges) { const auto& name = r.device->getName(); if (!name.empty()) { result.addListElement(name); } } } const MSXMultiMemDevice::Range& MSXMultiMemDevice::searchRange(unsigned address) const { for (auto& r : ranges) { if (isInside(address, r.base, r.size)) { return r; } } UNREACHABLE; } MSXDevice* MSXMultiMemDevice::searchDevice(unsigned address) const { return searchRange(address).device; } byte MSXMultiMemDevice::readMem(word address, EmuTime::param time) { return searchDevice(address)->readMem(address, time); } byte MSXMultiMemDevice::peekMem(word address, EmuTime::param time) const { return searchDevice(address)->peekMem(address, time); } void MSXMultiMemDevice::writeMem(word address, byte value, EmuTime::param time) { searchDevice(address)->writeMem(address, value, time); } const byte* MSXMultiMemDevice::getReadCacheLine(word start) const { assert((start & CacheLine::HIGH) == start); // start is aligned // Because start is aligned we don't need to wory about the begin // address of the range. But we must make sure the end of the range // doesn't only fill a partial cacheline. const auto& range = searchRange(start); if (unlikely(((range.base + range.size) & CacheLine::HIGH) == start)) { // The end of this memory device only fills a partial // cacheline. This can't be cached. return nullptr; } return searchDevice(start)->getReadCacheLine(start); } byte* MSXMultiMemDevice::getWriteCacheLine(word start) const { assert((start & CacheLine::HIGH) == start); const auto& range = searchRange(start); if (unlikely(((range.base + range.size) & CacheLine::HIGH) == start)) { return nullptr; } return searchDevice(start)->getWriteCacheLine(start); } } // namespace openmsx openmsx-0.10.0/src/cpu/VDPIODelay.hh0000644000175000017500000000160212262345041017602 0ustar manuelmanuel00000000000000#ifndef VDPIODELAY_HH #define VDPIODELAY_HH #include "MSXDevice.hh" #include "Clock.hh" namespace openmsx { class MSXCPU; class MSXCPUInterface; class VDPIODelay : public MSXDevice { public: VDPIODelay(const DeviceConfig& config, MSXCPUInterface& cpuInterface); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); const MSXDevice& getInDevice(byte port) const; MSXDevice*& getInDevicePtr (byte port); MSXDevice*& getOutDevicePtr(byte port); template void serialize(Archive& ar, unsigned version); private: void delay(EmuTime::param time); MSXCPU& cpu; MSXDevice* inDevices[4]; MSXDevice* outDevices[4]; /** Remembers the time at which last VDP I/O action took place. */ Clock<7159090> lastTime; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/IRQHelper.cc0000644000175000017500000000270512262345041017530 0ustar manuelmanuel00000000000000#include "IRQHelper.hh" #include "MSXMotherBoard.hh" #include "MSXCPU.hh" #include "serialize.hh" namespace openmsx { // class IRQSource IRQSource::IRQSource(MSXCPU& cpu_) : cpu(cpu_) { } void IRQSource::raise() { cpu.raiseIRQ(); } void IRQSource::lower() { cpu.lowerIRQ(); } // class NMISource NMISource::NMISource(MSXCPU& cpu_) : cpu(cpu_) { } void NMISource::raise() { cpu.raiseNMI(); } void NMISource::lower() { cpu.lowerNMI(); } // class DynamicSource DynamicSource::DynamicSource(MSXCPU& cpu_) : cpu(cpu_), type(IRQ) { } void DynamicSource::raise() { if (type == IRQ) { cpu.raiseIRQ(); } else { cpu.raiseNMI(); } } void DynamicSource::lower() { if (type == NMI) { cpu.lowerIRQ(); } else { cpu.lowerNMI(); } } // class IntHelper template IntHelper::IntHelper(MSXMotherBoard& motherboard, const std::string& name) : SOURCE(motherboard.getCPU()) , request(motherboard.getDebugger(), name, "Outgoing IRQ signal.", false) { } template template void IntHelper::serialize(Archive& ar, unsigned /*version*/) { bool pending = request; ar.serialize("pending", pending); if (ar.isLoader()) { if (pending) { set(); } else { reset(); } } } INSTANTIATE_SERIALIZE_METHODS(IRQHelper); // Force template instantiation template class IntHelper; //template class IntHelper; //template class IntHelper; } // namespace openmsx openmsx-0.10.0/src/cpu/MSXMultiMemDevice.hh0000644000175000017500000000235412262345041021210 0ustar manuelmanuel00000000000000#ifndef MSXMULTIMEMDEVICE_HH #define MSXMULTIMEMDEVICE_HH #include "MSXMultiDevice.hh" #include namespace openmsx { class MSXCPUInterface; class MSXMultiMemDevice : public MSXMultiDevice { public: MSXMultiMemDevice(const HardwareConfig& hwConf); virtual ~MSXMultiMemDevice(); bool canAdd(int base, int size); void add(MSXDevice& device, int base, int size); void remove(MSXDevice& device, int base, int size); bool empty() const; std::vector getDevices() const; // MSXDevice virtual std::string getName() const; virtual void getNameList(TclObject& result) const; virtual byte readMem(word address, EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte* getWriteCacheLine(word start) const; private: struct Range { Range(unsigned base_, unsigned size_, MSXDevice& device_); bool operator==(const Range& other) const; unsigned base; unsigned size; MSXDevice* device; }; const Range& searchRange(unsigned address) const; MSXDevice* searchDevice(unsigned address) const; std::vector ranges; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/Dasm.cc0000644000175000017500000000474412262345041016626 0ustar manuelmanuel00000000000000#include "Dasm.hh" #include "DasmTables.hh" #include "MSXCPUInterface.hh" #include "StringOp.hh" namespace openmsx { static char sign(unsigned char a) { return (a & 128) ? '-' : '+'; } static int abs(unsigned char a) { return (a & 128) ? (256 - a) : a; } unsigned dasm(const MSXCPUInterface& interf, word pc, byte buf[4], std::string& dest, EmuTime::param time) { const char* s; unsigned i = 0; const char* r = nullptr; buf[0] = interf.peekMem(pc, time); switch (buf[0]) { case 0xCB: buf[1] = interf.peekMem(pc + 1, time); s = mnemonic_cb[buf[1]]; i = 2; break; case 0xED: buf[1] = interf.peekMem(pc + 1, time); s = mnemonic_ed[buf[1]]; i = 2; break; case 0xDD: case 0xFD: r = (buf[0] == 0xDD) ? "ix" : "iy"; buf[1] = interf.peekMem(pc + 1, time); if (buf[1] != 0xcb) { s = mnemonic_xx[buf[1]]; i = 2; } else { buf[2] = interf.peekMem(pc + 2, time); buf[3] = interf.peekMem(pc + 3, time); s = mnemonic_xx_cb[buf[3]]; i = 4; } break; default: s = mnemonic_main[buf[0]]; i = 1; } for (int j = 0; s[j]; ++j) { switch (s[j]) { case 'B': buf[i] = interf.peekMem(pc + i, time); dest += '#' + StringOp::toHexString( static_cast(buf[i]), 2); i += 1; break; case 'R': buf[i] = interf.peekMem(pc + i, time); dest += '#' + StringOp::toHexString( (pc + 2 + static_cast(buf[i])) & 0xFFFF, 4); i += 1; break; case 'W': buf[i + 0] = interf.peekMem(pc + i + 0, time); buf[i + 1] = interf.peekMem(pc + i + 1, time); dest += '#' + StringOp::toHexString(buf[i] + buf[i + 1] * 256, 4); i += 2; break; case 'X': buf[i] = interf.peekMem(pc + i, time); dest += '(' + std::string(r) + sign(buf[i]) + '#' + StringOp::toHexString(abs(buf[i]), 2) + ')'; i += 1; break; case 'Y': dest += std::string(r) + sign(buf[2]) + '#' + StringOp::toHexString(abs(buf[2]), 2); break; case 'I': dest += r; break; case '!': dest = "db #ED,#" + StringOp::toHexString(buf[1], 2) + " "; return 2; case '@': dest = "db #" + StringOp::toHexString(buf[0], 2) + " "; return 1; case '#': dest = "db #" + StringOp::toHexString(buf[0], 2) + ",#CB,#" + StringOp::toHexString(buf[2], 2) + ' '; return 2; case ' ': { dest.resize(7, ' '); break; } default: dest += s[j]; break; } } dest.resize(19, ' '); return i; } } // namespace openmsx openmsx-0.10.0/src/cpu/BreakPoint.hh0000644000175000017500000000107112262345041020000 0ustar manuelmanuel00000000000000#ifndef BREAKPOINT_HH #define BREAKPOINT_HH #include "BreakPointBase.hh" #include "openmsx.hh" namespace openmsx { /** Base class for CPU breakpoints. * For performance reasons every bp is associated with exactly one * (immutable) address. */ class BreakPoint : public BreakPointBase { public: BreakPoint(GlobalCliComm& CliComm, word address, TclObject command, TclObject condition); word getAddress() const; unsigned getId() const; private: const unsigned id; const word address; static unsigned lastId; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/MSXCPU.cc0000644000175000017500000002764312262345041016764 0ustar manuelmanuel00000000000000#include "MSXCPU.hh" #include "MSXMotherBoard.hh" #include "Debugger.hh" #include "Scheduler.hh" #include "SimpleDebuggable.hh" #include "BooleanSetting.hh" #include "IntegerSetting.hh" #include "TclCallback.hh" #include "CPUCore.hh" #include "Z80.hh" #include "R800.hh" #include "BreakPoint.hh" #include "InfoTopic.hh" #include "TclObject.hh" #include "serialize.hh" #include "unreachable.hh" #include "memory.hh" #include using std::string; using std::vector; namespace openmsx { class TimeInfoTopic : public InfoTopic { public: TimeInfoTopic(InfoCommand& machineInfoCommand, MSXCPU& msxcpu); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help (const vector& tokens) const; private: MSXCPU& msxcpu; }; class CPUFreqInfoTopic : public InfoTopic { public: CPUFreqInfoTopic(InfoCommand& machineInfoCommand, const string& name, CPUClock& clock); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help (const vector& tokens) const; private: CPUClock& clock; }; class MSXCPUDebuggable : public SimpleDebuggable { public: MSXCPUDebuggable(MSXMotherBoard& motherboard, MSXCPU& cpu); virtual byte read(unsigned address); virtual void write(unsigned address, byte value); private: MSXCPU& cpu; }; MSXCPU::MSXCPU(MSXMotherBoard& motherboard_) : motherboard(motherboard_) , traceSetting(make_unique( motherboard.getCommandController(), "cputrace", "CPU tracing on/off", false, Setting::DONT_SAVE)) , diHaltCallback(make_unique( motherboard.getCommandController(), "di_halt_callback", "Tcl proc called when the CPU executed a DI/HALT sequence")) , z80(make_unique>( motherboard, "z80", *traceSetting, *diHaltCallback, EmuTime::zero)) , r800(motherboard.isTurboR() ? make_unique>( motherboard, "r800", *traceSetting, *diHaltCallback, EmuTime::zero) : nullptr) , timeInfo(make_unique( motherboard.getMachineInfoCommand(), *this)) , z80FreqInfo(make_unique( motherboard.getMachineInfoCommand(), "z80_freq", *z80)) , r800FreqInfo(r800 ? make_unique( motherboard.getMachineInfoCommand(), "r800_freq", *r800) : nullptr) , debuggable(make_unique(motherboard_, *this)) , reference(EmuTime::zero) { z80Active = true; // setActiveCPU(CPU_Z80); newZ80Active = z80Active; motherboard.getDebugger().setCPU(this); motherboard.getScheduler().setCPU(this); traceSetting->attach(*this); z80->freqLocked->attach(*this); z80->freqValue->attach(*this); if (r800) { r800->freqLocked->attach(*this); r800->freqValue->attach(*this); } } MSXCPU::~MSXCPU() { traceSetting->detach(*this); z80->freqLocked->detach(*this); z80->freqValue->detach(*this); if (r800) { r800->freqLocked->detach(*this); r800->freqValue->detach(*this); } motherboard.getScheduler().setCPU(nullptr); motherboard.getDebugger() .setCPU(nullptr); } void MSXCPU::setInterface(MSXCPUInterface* interface) { z80 ->setInterface(interface); if (r800) r800->setInterface(interface); } void MSXCPU::doReset(EmuTime::param time) { z80 ->doReset(time); if (r800) r800->doReset(time); reference = time; } void MSXCPU::setActiveCPU(CPUType cpu) { bool tmp = cpu == CPU_Z80; switch (cpu) { case CPU_Z80: PRT_DEBUG("Active CPU: Z80"); break; case CPU_R800: PRT_DEBUG("Active CPU: R800"); assert(r800); break; default: UNREACHABLE; } if (tmp != z80Active) { exitCPULoopSync(); newZ80Active = tmp; } } void MSXCPU::setDRAMmode(bool dram) { assert(r800); r800->setDRAMmode(dram); } void MSXCPU::execute(bool fastForward) { if (z80Active != newZ80Active) { EmuTime time = getCurrentTime(); z80Active = newZ80Active; z80Active ? z80 ->warp(time) : r800->warp(time); invalidateMemCache(0x0000, 0x10000); } z80Active ? z80 ->execute(fastForward) : r800->execute(fastForward); } void MSXCPU::exitCPULoopSync() { z80Active ? z80 ->exitCPULoopSync() : r800->exitCPULoopSync(); } void MSXCPU::exitCPULoopAsync() { z80Active ? z80 ->exitCPULoopAsync() : r800->exitCPULoopAsync(); } EmuTime::param MSXCPU::getCurrentTime() const { return z80Active ? z80 ->getCurrentTime() : r800->getCurrentTime(); } void MSXCPU::setNextSyncPoint(EmuTime::param time) { z80Active ? z80 ->setNextSyncPoint(time) : r800->setNextSyncPoint(time); } void MSXCPU::updateVisiblePage(byte page, byte primarySlot, byte secondarySlot) { invalidateMemCache(page * 0x4000, 0x4000); if (r800) r800->updateVisiblePage(page, primarySlot, secondarySlot); } void MSXCPU::invalidateMemCache(word start, unsigned size) { z80Active ? z80 ->invalidateMemCache(start, size) : r800->invalidateMemCache(start, size); } void MSXCPU::raiseIRQ() { z80 ->raiseIRQ(); if (r800) r800->raiseIRQ(); } void MSXCPU::lowerIRQ() { z80 ->lowerIRQ(); if (r800) r800->lowerIRQ(); } void MSXCPU::raiseNMI() { z80 ->raiseNMI(); if (r800) r800->raiseNMI(); } void MSXCPU::lowerNMI() { z80 ->lowerNMI(); if (r800) r800->lowerNMI(); } bool MSXCPU::isM1Cycle(unsigned address) const { return z80Active ? z80 ->isM1Cycle(address) : r800->isM1Cycle(address); } void MSXCPU::setZ80Freq(unsigned freq) { z80->setFreq(freq); } void MSXCPU::wait(EmuTime::param time) { z80Active ? z80 ->wait(time) : r800->wait(time); } void MSXCPU::waitCycles(unsigned cycles) { z80Active ? z80 ->waitCycles(cycles) : r800->waitCycles(cycles); } void MSXCPU::waitCyclesR800(unsigned cycles) { if (isR800Active()) { r800->waitCycles(cycles); } } CPURegs& MSXCPU::getRegisters() { if (z80Active) { return *z80; } else { return *r800; } } void MSXCPU::update(const Setting& setting) { z80 ->update(setting); if (r800) r800->update(setting); exitCPULoopSync(); } // Command void MSXCPU::disasmCommand(const vector& tokens, TclObject& result) const { z80Active ? z80 ->disasmCommand(tokens, result) : r800->disasmCommand(tokens, result); } void MSXCPU::setPaused(bool paused) { if (z80Active) { z80 ->setExtHALT(paused); z80 ->exitCPULoopSync(); } else { r800->setExtHALT(paused); r800->exitCPULoopSync(); } } // class TimeInfoTopic TimeInfoTopic::TimeInfoTopic(InfoCommand& machineInfoCommand, MSXCPU& msxcpu_) : InfoTopic(machineInfoCommand, "time") , msxcpu(msxcpu_) { } void TimeInfoTopic::execute(const vector& /*tokens*/, TclObject& result) const { EmuDuration dur = msxcpu.getCurrentTime() - msxcpu.reference; result.setDouble(dur.toDouble()); } string TimeInfoTopic::help(const vector& /*tokens*/) const { return "Prints the time in seconds that the MSX is powered on\n"; } // class CPUFreqInfoTopic CPUFreqInfoTopic::CPUFreqInfoTopic(InfoCommand& machineInfoCommand, const string& name, CPUClock& clock_) : InfoTopic(machineInfoCommand, name) , clock(clock_) { } void CPUFreqInfoTopic::execute(const vector& /*tokens*/, TclObject& result) const { result.setInt(clock.getFreq()); } string CPUFreqInfoTopic::help(const vector& /*tokens*/) const { return "Returns the actual frequency of this CPU.\n" "This frequency can vary because:\n" " - the user has overridden the freq via the '{z80,r800}_freq' setting\n" " - (only on some MSX machines) the MSX software can switch the Z80 between 2 frequencies\n" "See also the '{z80,r800}_freq_locked' setting.\n"; } // class MSXCPUDebuggable static const char* const CPU_REGS_DESC = "Registers of the active CPU (Z80 or R800).\n" "Each byte in this debuggable represents one 8 bit register:\n" " 0 -> A 1 -> F 2 -> B 3 -> C\n" " 4 -> D 5 -> E 6 -> H 7 -> L\n" " 8 -> A' 9 -> F' 10 -> B' 11 -> C'\n" " 12 -> D' 13 -> E' 14 -> H' 15 -> L'\n" " 16 -> IXH 17 -> IXL 18 -> IYH 19 -> IYL\n" " 20 -> PCH 21 -> PCL 22 -> SPH 23 -> SPL\n" " 24 -> I 25 -> R 26 -> IM 27 -> IFF1/2\n" "The last position (27) contains the IFF1 and IFF2 flags in respectively\n" "bit 0 and 1. Bit 2 contains 'IFF1 AND last-instruction-was-not-EI', so\n" "this effectively indicates that the CPU could accept an interrupt at\n" "the start of the current instruction.\n"; MSXCPUDebuggable::MSXCPUDebuggable(MSXMotherBoard& motherboard, MSXCPU& cpu_) : SimpleDebuggable(motherboard, "CPU regs", CPU_REGS_DESC, 28) , cpu(cpu_) { } byte MSXCPUDebuggable::read(unsigned address) { const CPURegs& regs = cpu.getRegisters(); switch (address) { case 0: return regs.getA(); case 1: return regs.getF(); case 2: return regs.getB(); case 3: return regs.getC(); case 4: return regs.getD(); case 5: return regs.getE(); case 6: return regs.getH(); case 7: return regs.getL(); case 8: return regs.getA2(); case 9: return regs.getF2(); case 10: return regs.getB2(); case 11: return regs.getC2(); case 12: return regs.getD2(); case 13: return regs.getE2(); case 14: return regs.getH2(); case 15: return regs.getL2(); case 16: return regs.getIXh(); case 17: return regs.getIXl(); case 18: return regs.getIYh(); case 19: return regs.getIYl(); case 20: return regs.getPCh(); case 21: return regs.getPCl(); case 22: return regs.getSPh(); case 23: return regs.getSPl(); case 24: return regs.getI(); case 25: return regs.getR(); case 26: return regs.getIM(); case 27: return 1 * regs.getIFF1() + 2 * regs.getIFF2() + 4 * (regs.getIFF1() && !regs.debugGetAfterEI()); default: UNREACHABLE; return 0; } } void MSXCPUDebuggable::write(unsigned address, byte value) { CPURegs& regs = cpu.getRegisters(); switch (address) { case 0: regs.setA(value); break; case 1: regs.setF(value); break; case 2: regs.setB(value); break; case 3: regs.setC(value); break; case 4: regs.setD(value); break; case 5: regs.setE(value); break; case 6: regs.setH(value); break; case 7: regs.setL(value); break; case 8: regs.setA2(value); break; case 9: regs.setF2(value); break; case 10: regs.setB2(value); break; case 11: regs.setC2(value); break; case 12: regs.setD2(value); break; case 13: regs.setE2(value); break; case 14: regs.setH2(value); break; case 15: regs.setL2(value); break; case 16: regs.setIXh(value); break; case 17: regs.setIXl(value); break; case 18: regs.setIYh(value); break; case 19: regs.setIYl(value); break; case 20: regs.setPCh(value); break; case 21: regs.setPCl(value); break; case 22: regs.setSPh(value); break; case 23: regs.setSPl(value); break; case 24: regs.setI(value); break; case 25: regs.setR(value); break; case 26: if (value < 3) regs.setIM(value); break; case 27: regs.setIFF1((value & 0x01) != 0); regs.setIFF2((value & 0x02) != 0); // can't change afterEI break; default: UNREACHABLE; } } // version 1: initial version // version 2: activeCPU,newCPU -> z80Active,newZ80Active template void MSXCPU::serialize(Archive& ar, unsigned version) { if (ar.versionAtLeast(version, 2)) { ar.serialize("z80", *z80); if (r800) ar.serialize("r800", *r800); ar.serialize("z80Active", z80Active); ar.serialize("newZ80Active", newZ80Active); } else { // backwards-compatibility assert(ar.isLoader()); ar.serializeWithID("z80", *z80); if (r800) ar.serializeWithID("r800", *r800); CPUBase* activeCPU = nullptr; CPUBase* newCPU = nullptr; ar.serializePointerID("activeCPU", activeCPU); ar.serializePointerID("newCPU", newCPU); z80Active = activeCPU == z80.get(); if (newCPU) { newZ80Active = newCPU == z80.get(); } else { newZ80Active = z80Active; } } ar.serialize("resetTime", reference); } INSTANTIATE_SERIALIZE_METHODS(MSXCPU); } // namespace openmsx openmsx-0.10.0/src/cpu/WatchPoint.cc0000644000175000017500000000110112262345041020002 0ustar manuelmanuel00000000000000#include "WatchPoint.hh" #include namespace openmsx { unsigned WatchPoint::lastId = 0; WatchPoint::WatchPoint(GlobalCliComm& cliComm, TclObject command, TclObject condition, Type type_, unsigned beginAddr_, unsigned endAddr_, unsigned newId /*= -1*/) : BreakPointBase(cliComm, command, condition) , id((newId == unsigned(-1)) ? ++lastId : newId) , beginAddr(beginAddr_), endAddr(endAddr_), type(type_) { assert(beginAddr <= endAddr); } WatchPoint::~WatchPoint() { } } // namespace openmsx openmsx-0.10.0/src/cpu/MSXMultiDevice.hh0000644000175000017500000000056512262345041020553 0ustar manuelmanuel00000000000000#ifndef MSXMULTIDEVICE_HH #define MSXMULTIDEVICE_HH #include "MSXDevice.hh" namespace openmsx { class MSXMultiDevice : public MSXDevice { public: explicit MSXMultiDevice(const HardwareConfig& hwConf); virtual void reset(EmuTime::param time); virtual void powerUp(EmuTime::param time); virtual void powerDown(EmuTime::param time); }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/CacheLine.hh0000644000175000017500000000054312262345041017560 0ustar manuelmanuel00000000000000#ifndef CACHELINE_HH #define CACHELINE_HH namespace openmsx { namespace CacheLine { static const unsigned BITS = 8; // 256 bytes static const unsigned SIZE = 1 << BITS; static const unsigned NUM = 0x10000 / SIZE; static const unsigned LOW = SIZE - 1; static const unsigned HIGH = 0xFFFF - LOW; } // namespace CacheLine } // namespace openmsx #endif openmsx-0.10.0/src/cpu/MSXMultiDevice.cc0000644000175000017500000000115512262345041020535 0ustar manuelmanuel00000000000000#include "MSXMultiDevice.hh" #include "DeviceConfig.hh" #include "XMLElement.hh" #include "unreachable.hh" namespace openmsx { static DeviceConfig getMultiConfig(const HardwareConfig& hwConf) { static XMLElement xml("Multi"); return DeviceConfig(hwConf, xml); } MSXMultiDevice::MSXMultiDevice(const HardwareConfig& hwConf) : MSXDevice(getMultiConfig(hwConf), "Multi") { } void MSXMultiDevice::reset(EmuTime::param /*time*/) { UNREACHABLE; } void MSXMultiDevice::powerUp(EmuTime::param /*time*/) { UNREACHABLE; } void MSXMultiDevice::powerDown(EmuTime::param /*time*/) { UNREACHABLE; } } // namespace openmsx openmsx-0.10.0/src/cpu/R800.hh0000644000175000017500000002061512262345041016400 0ustar manuelmanuel00000000000000#ifndef R800_HH #define R800_HH #include "CPUClock.hh" #include "CPURegs.hh" #include "Clock.hh" #include "likely.hh" #include "inline.hh" namespace openmsx { class R800TYPE : public CPUClock { public: void updateVisiblePage(byte page, byte primarySlot, byte secondarySlot) { extraMemoryDelay[page] = extraMemoryDelays[page][primarySlot][secondarySlot]; } void setDRAMmode(bool dram) { // TODO currently hardcoded, move to config file? unsigned val = dram ? 0 : 1; extraMemoryDelays[0][0][0] = val; // BIOS extraMemoryDelays[1][0][0] = val; // BASIC extraMemoryDelays[0][3][1] = val; // SUB-ROM extraMemoryDelays[1][3][1] = val; // KANJI-DRIVER } protected: template struct Normalize { static const bool value = B; }; static const int CLOCK_FREQ = 7159090; ALWAYS_INLINE unsigned haltStates() const { return 1; } // TODO check this ALWAYS_INLINE bool isR800() const { return true; } R800TYPE(EmuTime::param time, Scheduler& scheduler) : CPUClock(time, scheduler) , lastRefreshTime(time) { R800ForcePageBreak(); // TODO currently hardcoded, move to config file? for (int page = 0; page < 4; ++page) { for (int prim = 0; prim < 4; ++prim) { for (int sec = 0; sec < 4; ++sec) { unsigned val; if ((prim == 1) || (prim == 2)) { // external slot val = 2; } else if ((prim == 3) && (sec == 0)) { // internal RAM val = 0; } else { // internal ROM val = 1; } extraMemoryDelays[page][prim][sec] = val; } } } for (int page = 0; page < 4; ++page) { extraMemoryDelay[page] = extraMemoryDelays[page][0][0]; } } ALWAYS_INLINE void R800ForcePageBreak() { lastPage = -1; } template ALWAYS_INLINE void PRE_MEM(unsigned address) { int newPage = address >> 8; if (PRE_PB) { // there is a statically predictable page break at this // point -> 'add(1)' moved to static cost table } else { if (unlikely(newPage != lastPage) || unlikely(extraMemoryDelay[address >> 14])) { add(1); } } if (!POST_PB) { lastPage = newPage; } } template ALWAYS_INLINE void POST_MEM(unsigned address) { add(extraMemoryDelay[address >> 14]); if (POST_PB) { R800ForcePageBreak(); } } template ALWAYS_INLINE void PRE_WORD(unsigned address) { int newPage = address >> 8; if (PRE_PB) { // there is a statically predictable page break at this // point -> 'add(1)' moved to static cost table if (unlikely(extraMemoryDelay[address >> 14])) { add(1); } } else { if (unlikely(extraMemoryDelay[address >> 14])) { add(2); } else if (unlikely(newPage != lastPage)) { add(1); } } if (!POST_PB) { lastPage = newPage; } } template ALWAYS_INLINE void POST_WORD(unsigned address) { add(2 * extraMemoryDelay[address >> 14]); if (POST_PB) { R800ForcePageBreak(); } } ALWAYS_INLINE void R800Refresh(CPURegs& R) { // atoc documentation says refresh every 222 clocks // duration: 256/1024KB 13.5 clocks // 512KB 21.5 clocks // But 26/210 matches measurements much better // (loosly based on old measurements by Jon on his analogue scope) EmuTime time = getTimeFast(); if (unlikely(lastRefreshTime.getTicksTill_fast(time) >= 210)) { R800RefreshSlow(time, R); // slow-path not inline } } NEVER_INLINE void R800RefreshSlow(EmuTime::param time, CPURegs& R) { do { lastRefreshTime += 210; } while (unlikely(lastRefreshTime.getTicksTill_fast(time) >= 210)); waitForEvenCycle(0); add(25); R800ForcePageBreak(); // TODO check this R.incR(1); } void setTime(EmuTime::param time) { // Base class implementation. CPUClock::setTime(time); // Otherwise advance_fast() in R800Refresh() above, gets a too // large time interval. lastRefreshTime.reset(time); } ALWAYS_INLINE void setMemPtr(unsigned) { /* nothing*/ } ALWAYS_INLINE unsigned getMemPtr() const { return 0; } // dummy value static const int I = 6; // cycles for an I/O operation static const int O = 1; // wait for one cycle and wait for next even // clock cycle (to sync with slower IO bus) // the latter part must be implemented // dynamically (not here in static tables) static const int P = 1; // cycles for a (statically known) page-break static const int CC_LD_A_SS = 1+P+1, CC_LD_A_SS_1 = 1+P, CC_LD_A_NN = 3+P+1, CC_LD_A_NN_1 = 1, CC_LD_A_NN_2 = 3+P, CC_LD_A_I = 2, CC_LD_R_R = 1, CC_LD_R_N = 2, CC_LD_R_N_1 = 1, CC_LD_R_HL = 1+P+1, CC_LD_R_HL_1 = 1+P, CC_LD_R_XIX = 3+P+1, CC_LD_R_XIX_1 = 1, CC_LD_R_XIX_2 = 3+P, // +1 CC_LD_HL_R = 1+P+1, CC_LD_HL_R_1 = 1+P, CC_LD_HL_N = 2+P+1, CC_LD_HL_N_1 = 1, CC_LD_HL_N_2 = 2+P, CC_LD_SS_A = 1+P+1, CC_LD_SS_A_1 = 1+P, CC_LD_NN_A = 3+P+1, CC_LD_NN_A_1 = 1, CC_LD_NN_A_2 = 3+P, CC_LD_XIX_R = 3+P+1, CC_LD_XIX_R_1 = 1, CC_LD_XIX_R_2 = 3+P, // +1 CC_LD_XIX_N = 3+P+1, CC_LD_XIX_N_1 = 1, CC_LD_XIX_N_2 = 3+P, // +1 CC_LD_HL_XX = 3+P+2, CC_LD_HL_XX_1 = 1, CC_LD_HL_XX_2 = 3+P, CC_LD_SP_HL = 1, CC_LD_SS_NN = 3, CC_LD_SS_NN_1 = 1, CC_LD_XX_HL = 3+P+2, CC_LD_XX_HL_1 = 1, CC_LD_XX_HL_2 = 3+P, CC_CP_R = 1, CC_CP_N = 2, CC_CP_N_1 = 1, CC_CP_XHL = 1+P+1, CC_CP_XHL_1 = 1+P, CC_CP_XIX = 3+P+1, CC_CP_XIX_1 = 1, CC_CP_XIX_2 = 3+P, // +1 CC_INC_R = 1, CC_INC_XHL = 1+P+2+P+1, CC_INC_XHL_1 = 1, CC_INC_XHL_2 = 1+P+2+P, CC_INC_XIX = 3+P+2+P+1, CC_INC_XIX_1 = 1, EE_INC_XIX = 2, // +1 CC_INC_SS = 1, CC_ADD_HL_SS = 1, CC_ADC_HL_SS = 2, CC_LDI = 2+P+1+P+1, CC_LDI_1 = 2+P, CC_LDI_2 = 2+P+1+P, CC_LDIR = 2+P+1+P+1, CC_CPI = 2+P+2, CC_CPI_1 = 2+P, CC_CPIR = 2+P+3, // TODO check CC_PUSH = 2+P+2, CC_PUSH_1 = 2+P, CC_POP = 1+P+2, CC_POP_1 = 1+P, CC_CALL = 3+P+2, CC_CALL_1 = 1, EE_CALL = 1, CC_CALL_A = 3+P+2, CC_CALL_B = 3, CC_RST = 2+P+2, // TODO check CC_RET_A = 1+P+2, CC_RET_B = 1, EE_RET_C = 0, CC_RETN = 2+P+2, EE_RETN = 1, // TODO check CC_JP_A = 4, CC_JP_B = 3, CC_JP_1 = 1, CC_JP_HL = 2, CC_JR_A = 3, CC_JR_B = 2, CC_JR_1 = 1, CC_DJNZ = 3, EE_DJNZ = 0, CC_EX_SP_HL = 1+P+4, CC_EX_SP_HL_1 = 1+P, CC_EX_SP_HL_2 = 1+P+2, CC_BIT_R = 2, CC_BIT_XHL = 2+P+1, CC_BIT_XHL_1 = 2+P, CC_BIT_XIX = 3+P+1, CC_BIT_XIX_1 = 3+P, // +1 CC_SET_R = 2, CC_SET_XHL = 2+P+2+P+1, CC_SET_XHL_1 = 2+P, CC_SET_XHL_2 = 2+P+2+P, CC_SET_XIX = 3+P+2+P+1, EE_SET_XIX = 1, // +1 CC_RLA = 1, CC_RLD = 2+P+2+P+1, CC_RLD_1 = 2+P, CC_RLD_2 = 2+P+2+P, CC_IN_A_N = 2+O+I, CC_IN_A_N_1 = 1, CC_IN_A_N_2 = 2+O, CC_IN_R_C = 2+O+I, CC_IN_R_C_1 = 2+O, CC_INI = 2+O+I+P+1, CC_INI_1 = 2+O, CC_INI_2 = 2+O+I+P, CC_INIR = 2+O+I+P+1, // TODO check CC_OUT_N_A = 2+O+I, CC_OUT_N_A_1 = 1, CC_OUT_N_A_2 = 2+O, CC_OUT_C_R = 2+O+I, CC_OUT_C_R_1 = 2+O, CC_OUTI = 2+P+1+O+I, CC_OUTI_1 = 2+P, CC_OUTI_2 = 2+P+1+O, CC_OTIR = 2+P+1+O+I, // TODO check CC_EX = 1, CC_NOP = 1, CC_CCF = 1, CC_SCF = 1, CC_DAA = 1, CC_NEG = 2, CC_CPL = 1, CC_DI = 2, CC_EI = 1, CC_HALT = 1, // TODO check CC_IM = 3, CC_MULUB = 14, CC_MULUW = 36, CC_NMI = 1+P+2, EE_NMI_1 = -1, // TODO check CC_IRQ0 = 1+P+2, EE_IRQ0_1 = -1, // TODO check CC_IRQ1 = 1+P+2, EE_IRQ1_1 = -1, // TODO check CC_IRQ2 = 1+P+2+P+2, EE_IRQ2_1 = -1, CC_IRQ2_2 = 1+P+2+P, // TODO check CC_MAIN = 0, CC_DD = 1, CC_PREFIX = 1, CC_DD_CB = 1, // +1 EE_ED = 1, CC_RDMEM = 1, CC_WRMEM = 2; template void serialize(Archive& ar, unsigned version) { CPUClock::serialize(ar, version); ar.serialize("lastRefreshTime", lastRefreshTime); ar.serialize("lastPage", lastPage); ar.serialize("extraMemoryDelay", extraMemoryDelay); // don't serialize 'extraMemoryDelays', is initialized in // constructor and setDRAMmode() } private: Clock lastRefreshTime; int lastPage; unsigned extraMemoryDelays[4][4][4]; unsigned extraMemoryDelay[4]; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/CPURegs.cc0000644000175000017500000000231712262345041017204 0ustar manuelmanuel00000000000000#include "CPURegs.hh" #include "serialize.hh" namespace openmsx { // version 1: Initial version. // version 2: Replaced 'afterEI' boolean with 'after' byte // (holds both afterEI and afterLDAI information). template void CPURegs::serialize(Archive& ar, unsigned version) { ar.serialize("af", AF_.w); ar.serialize("bc", BC_.w); ar.serialize("de", DE_.w); ar.serialize("hl", HL_.w); ar.serialize("af2", AF2_.w); ar.serialize("bc2", BC2_.w); ar.serialize("de2", DE2_.w); ar.serialize("hl2", HL2_.w); ar.serialize("ix", IX_.w); ar.serialize("iy", IY_.w); ar.serialize("pc", PC_.w); ar.serialize("sp", SP_.w); ar.serialize("i", I_); byte r = getR(); ar.serialize("r", r); // combined R_ and R2_ if (ar.isLoader()) setR(r); ar.serialize("im", IM_); ar.serialize("iff1", IFF1_); ar.serialize("iff2", IFF2_); assert(isSameAfter()); if (ar.versionBelow(version, 2)) { bool afterEI = false; // initialize to avoid warning ar.serialize("afterEI", afterEI); clearNextAfter(); if (afterEI) setAfterEI(); } else { ar.serialize("after", afterNext_); } copyNextAfter(); ar.serialize("halt", HALT_); } INSTANTIATE_SERIALIZE_METHODS(CPURegs); } // namespace openmsx openmsx-0.10.0/src/cpu/CPUCore.hh0000644000175000017500000003200012262345041017176 0ustar manuelmanuel00000000000000#ifndef CPUCORE_HH #define CPUCORE_HH #include "CPURegs.hh" #include "CacheLine.hh" #include "Probe.hh" #include "EmuTime.hh" #include "serialize_meta.hh" #include "openmsx.hh" #include #include namespace openmsx { class MSXCPUInterface; class Scheduler; class MSXMotherBoard; class BooleanSetting; class IntegerSetting; class Setting; class TclCallback; class TclObject; enum Reg8 : int; enum Reg16 : int; class CPUBase {}; // only for bw-compat savestates template class CPUCore : public CPUBase, public CPURegs, public CPU_POLICY { public: CPUCore(MSXMotherBoard& motherboard, const std::string& name, const BooleanSetting& traceSetting, TclCallback& diHaltCallback, EmuTime::param time); void setInterface(MSXCPUInterface* interf); /** * Reset the CPU. */ void doReset(EmuTime::param time); void execute(bool fastForward); /** Request to exit the main CPU emulation loop. * This method may only be called from the main thread. The CPU loop * will immediately be exited (current instruction will be finished, * but no new instruction will be executed). */ void exitCPULoopSync(); /** Similar to exitCPULoopSync(), but this method may be called from * any thread. Although now the loop will only be exited 'soon'. */ void exitCPULoopAsync(); void warp(EmuTime::param time); EmuTime::param getCurrentTime() const; void wait(EmuTime::param time); void waitCycles(unsigned cycles); void setNextSyncPoint(EmuTime::param time); void invalidateMemCache(unsigned start, unsigned size); bool isM1Cycle(unsigned address) const; void disasmCommand(const std::vector& tokens, TclObject& result) const; /** * Raises the maskable interrupt count. * Devices should call MSXCPU::raiseIRQ instead, or use the IRQHelper class. */ void raiseIRQ(); /** * Lowers the maskable interrupt count. * Devices should call MSXCPU::lowerIRQ instead, or use the IRQHelper class. */ void lowerIRQ(); /** * Raises the non-maskable interrupt count. * Devices should call MSXCPU::raiseNMI instead, or use the IRQHelper class. */ void raiseNMI(); /** * Lowers the non-maskable interrupt count. * Devices should call MSXCPU::lowerNMI instead, or use the IRQHelper class. */ void lowerNMI(); /** * Change the clock freq. */ void setFreq(unsigned freq); template void serialize(Archive& ar, unsigned version); private: void execute2(bool fastForward); bool needExitCPULoop(); void setSlowInstructions(); void doSetFreq(); // Observer !! non-virtual !! void update(const Setting& setting); // memory cache const byte* readCacheLine[CacheLine::NUM]; byte* writeCacheLine[CacheLine::NUM]; bool readCacheTried [CacheLine::NUM]; bool writeCacheTried[CacheLine::NUM]; MSXMotherBoard& motherboard; Scheduler& scheduler; MSXCPUInterface* interface; const BooleanSetting& traceSetting; TclCallback& diHaltCallback; Probe IRQStatus; Probe IRQAccept; // dynamic freq std::unique_ptr freqLocked; std::unique_ptr freqValue; unsigned freq; // state machine variables int slowInstructions; int NMIStatus; /** * Set to true when there was a rising edge on the NMI line * (rising = non-active -> active). * Set to false when the CPU jumps to the NMI handler address. */ bool nmiEdge; volatile bool exitLoop; /** In sync with traceSetting.getBoolean(). */ bool tracingEnabled; /** 'normal' Z80 and Z80 in a turboR behave slightly different */ const bool isTurboR; inline void cpuTracePre(); inline void cpuTracePost(); void cpuTracePost_slow(); inline byte READ_PORT(unsigned port, unsigned cc); inline void WRITE_PORT(unsigned port, byte value, unsigned cc); template byte RDMEMslow(unsigned address, unsigned cc); template inline byte RDMEM_impl2(unsigned address, unsigned cc); template inline byte RDMEM_impl (unsigned address, unsigned cc); inline byte RDMEM_OPCODE(unsigned cc); inline byte RDMEM(unsigned address, unsigned cc); template unsigned RD_WORD_slow(unsigned address, unsigned cc); template inline unsigned RD_WORD_impl2(unsigned address, unsigned cc); template inline unsigned RD_WORD_impl (unsigned address, unsigned cc); inline unsigned RD_WORD_PC(unsigned cc); inline unsigned RD_WORD(unsigned address, unsigned cc); template void WRMEMslow(unsigned address, byte value, unsigned cc); template inline void WRMEM_impl2(unsigned address, byte value, unsigned cc); template inline void WRMEM_impl (unsigned address, byte value, unsigned cc); inline void WRMEM(unsigned address, byte value, unsigned cc); void WR_WORD_slow(unsigned address, unsigned value, unsigned cc); inline void WR_WORD(unsigned address, unsigned value, unsigned cc); template void WR_WORD_rev_slow(unsigned address, unsigned value, unsigned cc); template inline void WR_WORD_rev2(unsigned address, unsigned value, unsigned cc); template inline void WR_WORD_rev (unsigned address, unsigned value, unsigned cc); void executeInstructions(); inline void nmi(); inline void irq0(); inline void irq1(); inline void irq2(); void executeSlow(); template inline byte get8() const; template inline unsigned get16() const; template inline void set8 (byte x); template inline void set16(unsigned x); template inline int ld_R_R(); template inline int ld_sp_SS(); template inline int ld_SS_a(); template inline int ld_xhl_R(); template inline int ld_xix_R(); inline int ld_xhl_byte(); template inline int ld_xix_byte(); template inline int WR_NN_Y(unsigned reg); template inline int ld_xword_SS(); template inline int ld_xword_SS_ED(); template inline int ld_a_SS(); inline int ld_xbyte_a(); inline int ld_a_xbyte(); template inline int ld_R_byte(); template inline int ld_R_xhl(); template inline int ld_R_xix(); template inline unsigned RD_P_XX(); template inline int ld_SS_xword(); template inline int ld_SS_xword_ED(); template inline int ld_SS_word(); inline void ADC(byte reg); inline int adc_a_a(); template inline int adc_a_R(); inline int adc_a_byte(); inline int adc_a_xhl(); template inline int adc_a_xix(); inline void ADD(byte reg); inline int add_a_a(); template inline int add_a_R(); inline int add_a_byte(); inline int add_a_xhl(); template inline int add_a_xix(); inline void AND(byte reg); inline int and_a(); template inline int and_R(); inline int and_byte(); inline int and_xhl(); template inline int and_xix(); inline void CP(byte reg); inline int cp_a(); template inline int cp_R(); inline int cp_byte(); inline int cp_xhl(); template inline int cp_xix(); inline void OR(byte reg); inline int or_a(); template inline int or_R(); inline int or_byte(); inline int or_xhl(); template inline int or_xix(); inline void SBC(byte reg); inline int sbc_a_a(); template inline int sbc_a_R(); inline int sbc_a_byte(); inline int sbc_a_xhl(); template inline int sbc_a_xix(); inline void SUB(byte reg); inline int sub_a(); template inline int sub_R(); inline int sub_byte(); inline int sub_xhl(); template inline int sub_xix(); inline void XOR(byte reg); inline int xor_a(); template inline int xor_R(); inline int xor_byte(); inline int xor_xhl(); template inline int xor_xix(); inline byte DEC(byte reg); template inline int dec_R(); template inline int DEC_X(unsigned x); inline int dec_xhl(); template inline int dec_xix(); inline byte INC(byte reg); template inline int inc_R(); template inline int INC_X(unsigned x); inline int inc_xhl(); template inline int inc_xix(); template inline int adc_hl_SS(); inline int adc_hl_hl(); template inline int add_SS_TT(); template inline int add_SS_SS(); template inline int sbc_hl_SS(); inline int sbc_hl_hl(); template inline int dec_SS(); template inline int inc_SS(); template inline int bit_N_R(); template inline int bit_N_xhl(); template inline int bit_N_xix(unsigned a); template inline int res_N_R(); template inline byte RES_X(unsigned bit, unsigned addr); template inline int res_N_xhl(); template inline int res_N_xix_R(unsigned a); template inline int set_N_R(); template inline byte SET_X(unsigned bit, unsigned addr); template inline int set_N_xhl(); template inline int set_N_xix_R(unsigned a); inline byte RL(byte reg); template inline byte RL_X(unsigned x); template inline int rl_R(); inline int rl_xhl(); template inline int rl_xix_R(unsigned a); inline byte RLC(byte reg); template inline byte RLC_X(unsigned x); template inline int rlc_R(); inline int rlc_xhl(); template inline int rlc_xix_R(unsigned a); inline byte RR(byte reg); template inline byte RR_X(unsigned x); template inline int rr_R(); inline int rr_xhl(); template inline int rr_xix_R(unsigned a); inline byte RRC(byte reg); template inline byte RRC_X(unsigned x); template inline int rrc_R(); inline int rrc_xhl(); template inline int rrc_xix_R(unsigned a); inline byte SLA(byte reg); template inline byte SLA_X(unsigned x); template inline int sla_R(); inline int sla_xhl(); template inline int sla_xix_R(unsigned a); inline byte SLL(byte reg); template inline byte SLL_X(unsigned x); template inline int sll_R(); inline int sll_xhl(); template inline int sll_xix_R(unsigned a); inline int sll2(); inline byte SRA(byte reg); template inline byte SRA_X(unsigned x); template inline int sra_R(); inline int sra_xhl(); template inline int sra_xix_R(unsigned a); inline byte SRL(byte reg); template inline byte SRL_X(unsigned x); template inline int srl_R(); inline int srl_xhl(); template inline int srl_xix_R(unsigned a); inline int rla(); inline int rlca(); inline int rra(); inline int rrca(); inline int rld(); inline int rrd(); template inline void PUSH(unsigned reg); template inline int push_SS(); template inline unsigned POP(); template inline int pop_SS(); template inline int call(COND cond); template inline int rst(); template inline int RET(COND cond); template inline int ret(COND cond); inline int ret(); inline int retn(); template inline int jp_SS(); template inline int jp(COND cond); template inline int jr(COND cond); inline int djnz(); template inline int ex_xsp_SS(); template inline int in_R_c(); inline int in_a_byte(); template inline int out_c_R(); inline int out_c_0(); inline int out_byte_a(); inline int BLOCK_CP(int increase, bool repeat); inline int cpd(); inline int cpi(); inline int cpdr(); inline int cpir(); inline int BLOCK_LD(int increase, bool repeat); inline int ldd(); inline int ldi(); inline int lddr(); inline int ldir(); inline int BLOCK_IN(int increase, bool repeat); inline int ind(); inline int ini(); inline int indr(); inline int inir(); inline int BLOCK_OUT(int increase, bool repeat); inline int outd(); inline int outi(); inline int otdr(); inline int otir(); inline int nop(); inline int ccf(); inline int cpl(); inline int daa(); inline int neg(); inline int scf(); inline int ex_af_af(); inline int ex_de_hl(); inline int exx(); inline int di(); inline int ei(); inline int halt(); template inline int im_N(); template inline int ld_a_IR(); inline int ld_r_a(); inline int ld_i_a(); template int mulub_a_R(); template int muluw_hl_SS(); friend class MSXCPU; friend class Z80TYPE; friend class R800TYPE; }; class Z80TYPE; class R800TYPE; SERIALIZE_CLASS_VERSION(CPUCore, 3); SERIALIZE_CLASS_VERSION(CPUCore, 3); // keep these two the same } // namespace openmsx #endif openmsx-0.10.0/src/cpu/MSXWatchIODevice.hh0000644000175000017500000000225712262345041020757 0ustar manuelmanuel00000000000000#ifndef MSXWATCHIODEVICE_HH #define MSXWATCHIODEVICE_HH #include "MSXMultiDevice.hh" #include "WatchPoint.hh" #include namespace openmsx { class MSXCPUInterface; class MSXWatchIODevice; class WatchIO : public WatchPoint { public: WatchIO(MSXMotherBoard& motherboard, WatchPoint::Type type, unsigned beginAddr, unsigned endAddr, TclObject command, TclObject condition, unsigned newId = -1); virtual ~WatchIO(); MSXWatchIODevice& getDevice(byte port); private: void doReadCallback(unsigned port); void doWriteCallback(unsigned port, unsigned value); MSXCPUInterface& cpuInterface; std::vector> ios; friend class MSXWatchIODevice; }; class MSXWatchIODevice : public MSXMultiDevice { public: MSXWatchIODevice(const HardwareConfig& hwConf, WatchIO& watchIO); MSXDevice*& getDevicePtr(); private: // MSXDevice virtual std::string getName() const; virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); WatchIO& watchIO; MSXDevice* device; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/BreakPointBase.cc0000644000175000017500000000202312262345041020557 0ustar manuelmanuel00000000000000#include "BreakPointBase.hh" #include "TclObject.hh" #include "CommandException.hh" #include "GlobalCliComm.hh" #include "ScopedAssign.hh" namespace openmsx { BreakPointBase::BreakPointBase(GlobalCliComm& cliComm_, TclObject command_, TclObject condition_) : cliComm(cliComm_), command(command_), condition(condition_) , executing(false) { } bool BreakPointBase::isTrue() const { if (condition.getString().empty()) { // unconditional bp return true; } try { return condition.evalBool(); } catch (CommandException& e) { cliComm.printWarning(e.getMessage()); return false; } } void BreakPointBase::checkAndExecute() { if (executing) { // no recursive execution return; } ScopedAssign sa(executing, true); if (isTrue()) { try { command.executeCommand(true); // compile command } catch (CommandException& e) { cliComm.printWarning(e.getMessage()); } } } Tcl_Interp* BreakPointBase::getInterpreter() const { return command.getInterpreter(); } } // namespace openmsx openmsx-0.10.0/src/cpu/MSXWatchIODevice.cc0000644000175000017500000000602112262345041020736 0ustar manuelmanuel00000000000000#include "MSXWatchIODevice.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" #include "MSXCPUInterface.hh" #include "TclObject.hh" #include "StringOp.hh" #include #include namespace openmsx { // class WatchIO WatchIO::WatchIO(MSXMotherBoard& motherboard, WatchPoint::Type type, unsigned beginAddr, unsigned endAddr, TclObject command, TclObject condition, unsigned newId /*= -1*/) : WatchPoint(motherboard.getReactor().getGlobalCliComm(), command, condition, type, beginAddr, endAddr, newId) , cpuInterface(motherboard.getCPUInterface()) { for (unsigned i = byte(beginAddr); i <= byte(endAddr); ++i) { ios.push_back(make_unique( *motherboard.getMachineConfig(), *this)); } } WatchIO::~WatchIO() { } MSXWatchIODevice& WatchIO::getDevice(byte port) { byte begin = getBeginAddress(); return *ios[port - begin]; } void WatchIO::doReadCallback(unsigned port) { if (cpuInterface.isFastForward()) return; Tcl_Interp* interp = getInterpreter(); Tcl_SetVar(interp, "wp_last_address", StringOp::toString(port).c_str(), TCL_GLOBAL_ONLY); // keep this object alive by holding a shared_ptr to it, for the case // this watchpoint deletes itself in checkAndExecute() // TODO can be implemented more efficiently by using // std::shared_ptr::shared_from_this MSXCPUInterface::WatchPoints wpCopy(cpuInterface.getWatchPoints()); checkAndExecute(); Tcl_UnsetVar(interp, "wp_last_address", TCL_GLOBAL_ONLY); } void WatchIO::doWriteCallback(unsigned port, unsigned value) { if (cpuInterface.isFastForward()) return; Tcl_Interp* interp = getInterpreter(); Tcl_SetVar(interp, "wp_last_address", StringOp::toString(port).c_str(), TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "wp_last_value", StringOp::toString(value).c_str(), TCL_GLOBAL_ONLY); // see comment in doReadCallback() above MSXCPUInterface::WatchPoints wpCopy(cpuInterface.getWatchPoints()); checkAndExecute(); Tcl_UnsetVar(interp, "wp_last_address", TCL_GLOBAL_ONLY); Tcl_UnsetVar(interp, "wp_last_value", TCL_GLOBAL_ONLY); } // class MSXWatchIODevice MSXWatchIODevice::MSXWatchIODevice( const HardwareConfig& hwConf, WatchIO& watchIO_) : MSXMultiDevice(hwConf) , watchIO(watchIO_) , device(nullptr) { } MSXDevice*& MSXWatchIODevice::getDevicePtr() { return device; } std::string MSXWatchIODevice::getName() const { assert(device); return device->getName(); } byte MSXWatchIODevice::peekIO(word port, EmuTime::param time) const { assert(device); return device->peekIO(port, time); } byte MSXWatchIODevice::readIO(word port, EmuTime::param time) { assert(device); // first trigger watchpoint, then read from device watchIO.doReadCallback(port); return device->readIO(port, time); } void MSXWatchIODevice::writeIO(word port, byte value, EmuTime::param time) { assert(device); // first write to device, then trigger watchpoint device->writeIO(port, value, time); watchIO.doWriteCallback(port, value); } } // namespace openmsx openmsx-0.10.0/src/cpu/Dasm.hh0000644000175000017500000000134712262345041016634 0ustar manuelmanuel00000000000000#ifndef DASM_HH #define DASM_HH #include "EmuTime.hh" #include "openmsx.hh" #include class MSXCPUInterface; namespace openmsx { /** Disassemble * @param interf The CPU interface, used to peek bytes from memory * @param pc The position (program counter) where to start disassembling * @param buf The bytes that form this opcode (max 4). The buffer is only * filled with the min required bytes (see return value) * @param dest String representation of the disassembled opcode * @param time TODO * @return Length of the disassembled opcode in bytes */ unsigned dasm(const MSXCPUInterface& interf, word pc, byte buf[4], std::string& dest, EmuTime::param time); } // namespace openmsx #endif openmsx-0.10.0/src/cpu/MSXCPUInterface.cc0000644000175000017500000010434512262345041020600 0ustar manuelmanuel00000000000000#include "MSXCPUInterface.hh" #include "BreakPoint.hh" #include "DebugCondition.hh" #include "DummyDevice.hh" #include "SimpleDebuggable.hh" #include "InfoTopic.hh" #include "CommandException.hh" #include "TclObject.hh" #include "Reactor.hh" #include "MSXMotherBoard.hh" #include "MSXCPU.hh" #include "VDPIODelay.hh" #include "CliComm.hh" #include "MSXMultiIODevice.hh" #include "MSXMultiMemDevice.hh" #include "MSXWatchIODevice.hh" #include "MSXException.hh" #include "CartridgeSlotManager.hh" #include "EventDistributor.hh" #include "Event.hh" #include "DeviceFactory.hh" #include "ReadOnlySetting.hh" #include "BooleanSetting.hh" #include "serialize.hh" #include "StringOp.hh" #include "checked_cast.hh" #include "unreachable.hh" #include "memory.hh" #include #include #include #include #include #include #include #include using std::ostringstream; using std::setfill; using std::setw; using std::uppercase; using std::string; using std::vector; using std::min; using std::shared_ptr; namespace openmsx { class MemoryDebug : public SimpleDebuggable { public: MemoryDebug(MSXCPUInterface& interface, MSXMotherBoard& motherBoard); virtual byte read(unsigned address, EmuTime::param time); virtual void write(unsigned address, byte value, EmuTime::param time); private: MSXCPUInterface& interface; }; class SlottedMemoryDebug : public SimpleDebuggable { public: SlottedMemoryDebug(MSXCPUInterface& interface, MSXMotherBoard& motherBoard); virtual byte read(unsigned address, EmuTime::param time); virtual void write(unsigned address, byte value, EmuTime::param time); private: MSXCPUInterface& interface; }; class IODebug : public SimpleDebuggable { public: IODebug(MSXCPUInterface& interface, MSXMotherBoard& motherBoard); virtual byte read(unsigned address, EmuTime::param time); virtual void write(unsigned address, byte value, EmuTime::param time); private: MSXCPUInterface& interface; }; class SlotInfo : public InfoTopic { public: SlotInfo(InfoCommand& machineInfoCommand, MSXCPUInterface& interface); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; private: MSXCPUInterface& interface; }; class SubSlottedInfo : public InfoTopic { public: SubSlottedInfo(InfoCommand& machineInfoCommand, MSXCPUInterface& interface); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; private: MSXCPUInterface& interface; }; class ExternalSlotInfo : public InfoTopic { public: ExternalSlotInfo(InfoCommand& machineInfoCommand, CartridgeSlotManager& manager); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; private: CartridgeSlotManager& manager; }; class IOInfo : public InfoTopic { public: IOInfo(InfoCommand& machineInfoCommand, MSXCPUInterface& interface, bool input); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; private: MSXCPUInterface& interface; bool input; }; // Global variables bool MSXCPUInterface::breaked = false; bool MSXCPUInterface::continued = false; bool MSXCPUInterface::step = false; MSXCPUInterface::BreakPoints MSXCPUInterface::breakPoints; //TODO watchpoints MSXCPUInterface::Conditions MSXCPUInterface::conditions; static std::unique_ptr breakedSetting; static unsigned breakedSettingCount = 0; // Bitfields used in the disallowReadCache and disallowWriteCache arrays static const byte SECUNDARY_SLOT_BIT = 0x01; static const byte MEMORY_WATCH_BIT = 0x02; static const byte GLOBAL_WRITE_BIT = 0x04; MSXCPUInterface::MSXCPUInterface(MSXMotherBoard& motherBoard_) : memoryDebug(make_unique( *this, motherBoard_)) , slottedMemoryDebug(make_unique( *this, motherBoard_)) , ioDebug(make_unique( *this, motherBoard_)) , slotInfo(make_unique( motherBoard_.getMachineInfoCommand(), *this)) , subSlottedInfo(make_unique( motherBoard_.getMachineInfoCommand(), *this)) , externalSlotInfo(make_unique( motherBoard_.getMachineInfoCommand(), motherBoard_.getSlotManager())) , inputPortInfo(make_unique( motherBoard_.getMachineInfoCommand(), *this, true)) , outputPortInfo(make_unique( motherBoard_.getMachineInfoCommand(), *this, false)) , dummyDevice(DeviceFactory::createDummyDevice( *motherBoard_.getMachineConfig())) , msxcpu(motherBoard_.getCPU()) , cliComm(motherBoard_.getMSXCliComm()) , motherBoard(motherBoard_) , fastForward(false) { for (int port = 0; port < 256; ++port) { IO_In [port] = dummyDevice.get(); IO_Out[port] = dummyDevice.get(); } for (int primSlot = 0; primSlot < 4; ++primSlot) { primarySlotState[primSlot] = 0; secondarySlotState[primSlot] = 0; expanded[primSlot] = 0; subSlotRegister[primSlot] = 0; for (int secSlot = 0; secSlot < 4; ++secSlot) { for (int page = 0; page < 4; ++page) { slotLayout[primSlot][secSlot][page] = dummyDevice.get(); } } } for (int page = 0; page < 4; ++page) { visibleDevices[page] = dummyDevice.get(); } // initially allow all regions to be cached memset(disallowReadCache, 0, sizeof(disallowReadCache)); memset(disallowWriteCache, 0, sizeof(disallowWriteCache)); // Note: SlotState is initialised at reset msxcpu.setInterface(this); if (motherBoard.isTurboR()) { // TODO also MSX2+ needs (slightly different) VDPIODelay delayDevice = DeviceFactory::createVDPIODelay( *motherBoard.getMachineConfig(), *this); for (int port = 0x98; port <= 0x9B; ++port) { assert(IO_In [port] == dummyDevice.get()); assert(IO_Out[port] == dummyDevice.get()); IO_In [port] = delayDevice.get(); IO_Out[port] = delayDevice.get(); } } if (breakedSettingCount++ == 0) { assert(!breakedSetting); breakedSetting = make_unique( motherBoard.getReactor().getCommandController(), "breaked", "Similar to 'debug breaked'", "false"); } } MSXCPUInterface::~MSXCPUInterface() { if (--breakedSettingCount == 0) { assert(breakedSetting); breakedSetting = nullptr; } removeAllWatchPoints(); if (delayDevice) { for (int port = 0x98; port <= 0x9B; ++port) { assert(IO_In [port] == delayDevice.get()); assert(IO_Out[port] == delayDevice.get()); IO_In [port] = dummyDevice.get(); IO_Out[port] = dummyDevice.get(); } } msxcpu.setInterface(nullptr); #ifndef NDEBUG for (int port = 0; port < 256; ++port) { if (IO_In[port] != dummyDevice.get()) { std::cout << "In-port " << port << " still registered " << IO_In[port]->getName() << std::endl; UNREACHABLE; } if (IO_Out[port] != dummyDevice.get()) { std::cout << "Out-port " << port << " still registered " << IO_Out[port]->getName() << std::endl; UNREACHABLE; } } for (int primSlot = 0; primSlot < 4; ++primSlot) { assert(!isExpanded(primSlot)); for (int secSlot = 0; secSlot < 4; ++secSlot) { for (int page = 0; page < 4; ++page) { assert(slotLayout[primSlot][secSlot][page] == dummyDevice.get()); } } } #endif } void MSXCPUInterface::removeAllWatchPoints() { while (!watchPoints.empty()) { removeWatchPoint(watchPoints.back()); } } byte MSXCPUInterface::readMemSlow(word address, EmuTime::param time) { // something special in this region? if (unlikely(disallowReadCache[address >> CacheLine::BITS])) { // execute read watches before actual read if (readWatchSet[address >> CacheLine::BITS] [address & CacheLine::LOW]) { executeMemWatch(WatchPoint::READ_MEM, address); } } if (unlikely((address == 0xFFFF) && isExpanded(primarySlotState[3]))) { return 0xFF ^ subSlotRegister[primarySlotState[3]]; } else { return visibleDevices[address >> 14]->readMem(address, time); } } void MSXCPUInterface::writeMemSlow(word address, byte value, EmuTime::param time) { if (unlikely((address == 0xFFFF) && isExpanded(primarySlotState[3]))) { setSubSlot(primarySlotState[3], value); } else { visibleDevices[address>>14]->writeMem(address, value, time); } // something special in this region? if (unlikely(disallowWriteCache[address >> CacheLine::BITS])) { // slot-select-ignore writes (Super Lode Runner) for (auto& g : globalWrites) { // very primitive address selection mechanism, // but more than enough for now if (unlikely(g.addr == address)) { g.device->globalWrite(address, value, time); } } // execute write watches after actual write if (writeWatchSet[address >> CacheLine::BITS] [address & CacheLine::LOW]) { executeMemWatch(WatchPoint::WRITE_MEM, address, value); } } } void MSXCPUInterface::setExpanded(int ps) { if (expanded[ps] == 0) { for (int page = 0; page < 4; ++page) { if (slotLayout[ps][0][page] != dummyDevice.get()) { throw MSXException("Can't expand slot because " "it's already in use."); } } } expanded[ps]++; changeExpanded(isExpanded(primarySlotState[3])); } void MSXCPUInterface::testUnsetExpanded( int ps, vector allowed) const { // TODO handle multi-devices allowed.push_back(dummyDevice.get()); sort(allowed.begin(), allowed.end()); // for set_difference() assert(isExpanded(ps)); if (expanded[ps] != 1) return; // ok, still expanded after this std::vector inUse; for (int ss = 0; ss < 4; ++ss) { for (int page = 0; page < 4; ++page) { MSXDevice* device = slotLayout[ps][ss][page]; std::vector devices; if (auto memDev = dynamic_cast(device)) { devices = memDev->getDevices(); sort(devices.begin(), devices.end()); // for set_difference() } else { devices.push_back(device); } std::set_difference(devices.begin(), devices.end(), allowed.begin(), allowed.end(), std::inserter(inUse, inUse.end())); } } if (inUse.empty()) return; // ok, no more devices in use StringOp::Builder msg; msg << "Can't remove slot expander from slot " << ps << " because the following devices are still inserted:"; for (auto& d : inUse) { msg << " " << d->getName(); } msg << '.'; throw MSXException(msg); } void MSXCPUInterface::unsetExpanded(int ps) { #ifndef NDEBUG try { vector dummy; testUnsetExpanded(ps, dummy); } catch (...) { UNREACHABLE; } #endif expanded[ps]--; changeExpanded(isExpanded(primarySlotState[3])); } void MSXCPUInterface::changeExpanded(bool isExpanded) { if (isExpanded) { disallowReadCache [0xFF] |= SECUNDARY_SLOT_BIT; disallowWriteCache[0xFF] |= SECUNDARY_SLOT_BIT; } else { disallowReadCache [0xFF] &= ~SECUNDARY_SLOT_BIT; disallowWriteCache[0xFF] &= ~SECUNDARY_SLOT_BIT; } msxcpu.invalidateMemCache(0xFFFF & CacheLine::HIGH, 0x100); } MSXDevice*& MSXCPUInterface::getDevicePtr(byte port, bool isIn) { MSXDevice** devicePtr = isIn ? &IO_In[port] : &IO_Out[port]; while (auto watch = dynamic_cast(*devicePtr)) { devicePtr = &watch->getDevicePtr(); } if (*devicePtr == delayDevice.get()) { devicePtr = isIn ? &delayDevice->getInDevicePtr (port) : &delayDevice->getOutDevicePtr(port); } return *devicePtr; } void MSXCPUInterface::register_IO_In(byte port, MSXDevice* device) { MSXDevice*& devicePtr = getDevicePtr(port, true); // in register_IO(port, true, devicePtr, device); // in } void MSXCPUInterface::unregister_IO_In(byte port, MSXDevice* device) { MSXDevice*& devicePtr = getDevicePtr(port, true); // in unregister_IO(devicePtr, device); } void MSXCPUInterface::register_IO_Out(byte port, MSXDevice* device) { MSXDevice*& devicePtr = getDevicePtr(port, false); // out register_IO(port, false, devicePtr, device); // out } void MSXCPUInterface::unregister_IO_Out(byte port, MSXDevice* device) { MSXDevice*& devicePtr = getDevicePtr(port, false); // out unregister_IO(devicePtr, device); } void MSXCPUInterface::register_IO(int port, bool isIn, MSXDevice*& devicePtr, MSXDevice* device) { PRT_DEBUG(device->getName() << " registers " << (isIn ? "In" : "Out") << "-port " << std::hex << port << std::dec); if (devicePtr == dummyDevice.get()) { // first, replace DummyDevice devicePtr = device; } else { if (auto multi = dynamic_cast(devicePtr)) { // third or more, add to existing MultiIO device multi->addDevice(device); } else { // second, create a MultiIO device multi = new MSXMultiIODevice(device->getHardwareConfig()); multi->addDevice(devicePtr); multi->addDevice(device); devicePtr = multi; } if (isIn) { cliComm.printWarning( "Conflicting input port 0x" + StringOp::toHexString(port, 2) + " for devices " + devicePtr->getName()); } } } void MSXCPUInterface::unregister_IO(MSXDevice*& devicePtr, MSXDevice* device) { if (auto multi = dynamic_cast(devicePtr)) { // remove from MultiIO device multi->removeDevice(device); auto& devices = multi->getDevices(); if (devices.size() == 1) { // only one remaining, remove MultiIO device devicePtr = devices.front(); devices.pop_back(); delete multi; } } else { // remove last, put back DummyDevice assert(devicePtr == device); devicePtr = dummyDevice.get(); } } static void reportMemOverlap(int ps, int ss, MSXDevice& dev1, MSXDevice& dev2) { throw MSXException(StringOp::Builder() << "Overlapping memory devices in slot " << ps << '.' << ss << ": " << dev1.getName() << " and " << dev2.getName() << '.'); } void MSXCPUInterface::testRegisterSlot( MSXDevice& device, int ps, int ss, int base, int size) { int page = base >> 14; MSXDevice*& slot = slotLayout[ps][ss][page]; if (size == 0x4000) { // full 16kb, directly register device (no multiplexer) if (slot != dummyDevice.get()) { reportMemOverlap(ps, ss, *slot, device); } } else { // partial page if (slot == dummyDevice.get()) { // first, ok } else if (auto multi = dynamic_cast(slot)) { // second (or more), check for overlap if (!multi->canAdd(base, size)) { reportMemOverlap(ps, ss, *slot, device); } } else { // conflict with 'full ranged' device reportMemOverlap(ps, ss, *slot, device); } } } void MSXCPUInterface::registerSlot( MSXDevice& device, int ps, int ss, int base, int size) { PRT_DEBUG(device.getName() << " registers at " << std::dec << ps << ' ' << ss << " 0x" << std::hex << base << "-0x" << (base + size - 1)); int page = base >> 14; MSXDevice*& slot = slotLayout[ps][ss][page]; if (size == 0x4000) { // full 16kb, directly register device (no multiplexer) assert(slot == dummyDevice.get()); slot = &device; } else { // partial page if (slot == dummyDevice.get()) { // first MSXMultiMemDevice* multi = new MSXMultiMemDevice(device.getHardwareConfig()); multi->add(device, base, size); slot = multi; } else if (auto multi = dynamic_cast(slot)) { // second or more assert(multi->canAdd(base, size)); multi->add(device, base, size); } else { // conflict with 'full ranged' device assert(false); } } updateVisible(page); } void MSXCPUInterface::unregisterSlot( MSXDevice& device, int ps, int ss, int base, int size) { int page = base >> 14; MSXDevice*& slot = slotLayout[ps][ss][page]; if (auto multi = dynamic_cast(slot)) { // partial range multi->remove(device, base, size); if (multi->empty()) { delete multi; slot = dummyDevice.get(); } } else { // full 16kb range assert(slot == &device); slot = dummyDevice.get(); } updateVisible(page); } void MSXCPUInterface::registerMemDevice( MSXDevice& device, int ps, int ss, int base_, int size_) { if (!isExpanded(ps) && (ss != 0)) { throw MSXException(StringOp::Builder() << "Slot " << ps << '.' << ss << " does not exist because slot is not expanded."); } // split range on 16kb borders // first check if registration is possible int base = base_; int size = size_; while (size > 0) { int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base); testRegisterSlot(device, ps, ss, base, partialSize); base += partialSize; size -= partialSize; } // if all checks are successful, only then actually register base = base_; size = size_; while (size > 0) { int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base); registerSlot(device, ps, ss, base, partialSize); base += partialSize; size -= partialSize; } } void MSXCPUInterface::unregisterMemDevice( MSXDevice& device, int ps, int ss, int base, int size) { // split range on 16kb borders while (size > 0) { int partialSize = min(size, ((base + 0x4000) & ~0x3FFF) - base); unregisterSlot(device, ps, ss, base, partialSize); base += partialSize; size -= partialSize; } } void MSXCPUInterface::registerGlobalWrite(MSXDevice& device, word address) { GlobalWriteInfo info = { &device, address }; globalWrites.push_back(info); disallowWriteCache[address >> CacheLine::BITS] |= GLOBAL_WRITE_BIT; msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100); } void MSXCPUInterface::unregisterGlobalWrite(MSXDevice& device, word address) { GlobalWriteInfo info = { &device, address }; auto it = find(globalWrites.begin(), globalWrites.end(), info); assert(it != globalWrites.end()); globalWrites.erase(it); for (auto& g : globalWrites) { if ((g.addr >> CacheLine::BITS) == (address >> CacheLine::BITS)) { // there is still a global write in this region return; } } disallowWriteCache[address >> CacheLine::BITS] &= ~GLOBAL_WRITE_BIT; msxcpu.invalidateMemCache(address & CacheLine::HIGH, 0x100); } ALWAYS_INLINE void MSXCPUInterface::updateVisible(int page, int ps, int ss) { MSXDevice* newDevice = slotLayout[ps][ss][page]; if (visibleDevices[page] != newDevice) { visibleDevices[page] = newDevice; msxcpu.updateVisiblePage(page, ps, ss); } } void MSXCPUInterface::updateVisible(int page) { updateVisible(page, primarySlotState[page], secondarySlotState[page]); } void MSXCPUInterface::reset() { for (int i = 0; i < 4; ++i) { subSlotRegister[i] = 0; } setPrimarySlots(0); } byte MSXCPUInterface::readIRQVector() { return motherBoard.readIRQVector(); } void MSXCPUInterface::setPrimarySlots(byte value) { // Change the slot structure. // Originally the code below was a loop over the 4 pages, and the check // for (un)expanded-slot was done unconditionally at the end. I've // completely unrolled the loop and only check for (un)expanded slot // when the slot in page 3 has changed. I've also added checks for slot // changes for the other 3 pages. Usually when this register is written // only one of the 4 pages actually changes, so these extra checks do // pay off. This does make the code a bit more complex (and the // generated code slightly bigger), but it does make a measurable speed // difference. Changing the slots several hundreds of times per // (EmuTime) is not unusual. So this routine ended up quite high // (top-10) in some profile results. int ps0 = (value >> 0) & 3; if (unlikely(primarySlotState[0] != ps0)) { primarySlotState[0] = ps0; int ss0 = (subSlotRegister[ps0] >> 0) & 3; secondarySlotState[0] = ss0; updateVisible(0, ps0, ss0); } int ps1 = (value >> 2) & 3; if (unlikely(primarySlotState[1] != ps1)) { primarySlotState[1] = ps1; int ss1 = (subSlotRegister[ps1] >> 2) & 3; secondarySlotState[1] = ss1; updateVisible(1, ps1, ss1); } int ps2 = (value >> 4) & 3; if (unlikely(primarySlotState[2] != ps2)) { primarySlotState[2] = ps2; int ss2 = (subSlotRegister[ps2] >> 4) & 3; secondarySlotState[2] = ss2; updateVisible(2, ps2, ss2); } int ps3 = (value >> 6) & 3; if (unlikely(primarySlotState[3] != ps3)) { bool oldExpanded = isExpanded(primarySlotState[3]); bool newExpanded = isExpanded(ps3); primarySlotState[3] = ps3; int ss3 = (subSlotRegister[ps3] >> 6) & 3; secondarySlotState[3] = ss3; updateVisible(3, ps3, ss3); if (unlikely(oldExpanded != newExpanded)) { changeExpanded(newExpanded); } } } void MSXCPUInterface::setSubSlot(byte primSlot, byte value) { subSlotRegister[primSlot] = value; for (int page = 0; page < 4; ++page, value >>= 2) { if (primSlot == primarySlotState[page]) { secondarySlotState[page] = value & 3; // Change the visible devices updateVisible(page); } } } byte MSXCPUInterface::peekMem(word address, EmuTime::param time) const { if ((address == 0xFFFF) && isExpanded(primarySlotState[3])) { return 0xFF ^ subSlotRegister[primarySlotState[3]]; } else { return visibleDevices[address >> 14]->peekMem(address, time); } } byte MSXCPUInterface::peekSlottedMem(unsigned address, EmuTime::param time) const { byte primSlot = (address & 0xC0000) >> 18; byte subSlot = (address & 0x30000) >> 16; byte page = (address & 0x0C000) >> 14; word offset = (address & 0xFFFF); // includes page if (!isExpanded(primSlot)) { subSlot = 0; } if ((offset == 0xFFFF) && isExpanded(primSlot)) { return 0xFF ^ subSlotRegister[primSlot]; } else { return slotLayout[primSlot][subSlot][page]->peekMem(offset, time); } } byte MSXCPUInterface::readSlottedMem(unsigned address, EmuTime::param time) { byte primSlot = (address & 0xC0000) >> 18; byte subSlot = (address & 0x30000) >> 16; byte page = (address & 0x0C000) >> 14; word offset = (address & 0xFFFF); // includes page if (!isExpanded(primSlot)) { subSlot = 0; } if ((offset == 0xFFFF) && isExpanded(primSlot)) { return 0xFF ^ subSlotRegister[primSlot]; } else { return slotLayout[primSlot][subSlot][page]->peekMem(offset, time); } } void MSXCPUInterface::writeSlottedMem(unsigned address, byte value, EmuTime::param time) { byte primSlot = (address & 0xC0000) >> 18; byte subSlot = (address & 0x30000) >> 16; byte page = (address & 0x0C000) >> 14; word offset = (address & 0xFFFF); // includes page if (!isExpanded(primSlot)) { subSlot = 0; } if ((offset == 0xFFFF) && isExpanded(primSlot)) { setSubSlot(primSlot, value); } else { slotLayout[primSlot][subSlot][page]->writeMem(offset, value, time); } } DummyDevice& MSXCPUInterface::getDummyDevice() { return *dummyDevice; } void MSXCPUInterface::insertBreakPoint(const shared_ptr& bp) { breakPoints.insert(std::make_pair(bp->getAddress(), bp)); } void MSXCPUInterface::removeBreakPoint(const BreakPoint& bp) { auto range = breakPoints.equal_range(bp.getAddress()); for (auto it = range.first; it != range.second; ++it) { if (it->second.get() == &bp) { breakPoints.erase(it); break; } } } const MSXCPUInterface::BreakPoints& MSXCPUInterface::getBreakPoints() { return breakPoints; } void MSXCPUInterface::checkBreakPoints( std::pair range) { // create copy for the case that breakpoint/condition removes itself // - keeps object alive by holding a shared_ptr to it // - avoids iterating over a changing collection BreakPoints bpCopy(range.first, range.second); for (auto& p : bpCopy) { p.second->checkAndExecute(); } auto condCopy = conditions; for (auto& c : condCopy) { c->checkAndExecute(); } } void MSXCPUInterface::setWatchPoint(const shared_ptr& watchPoint) { watchPoints.push_back(watchPoint); WatchPoint::Type type = watchPoint->getType(); switch (type) { case WatchPoint::READ_IO: registerIOWatch(*watchPoint, IO_In); break; case WatchPoint::WRITE_IO: registerIOWatch(*watchPoint, IO_Out); break; case WatchPoint::READ_MEM: case WatchPoint::WRITE_MEM: updateMemWatch(type); break; default: UNREACHABLE; break; } } void MSXCPUInterface::removeWatchPoint(shared_ptr watchPoint) { // Pass shared_ptr by value to keep the object alive for the duration // of this function, otherwise it gets deleted as soon as it's removed // from the watchPoints collection. for (auto it = watchPoints.begin(); it != watchPoints.end(); ++it) { if (*it == watchPoint) { // remove before calling updateMemWatch() watchPoints.erase(it); WatchPoint::Type type = watchPoint->getType(); switch (type) { case WatchPoint::READ_IO: unregisterIOWatch(*watchPoint, IO_In); break; case WatchPoint::WRITE_IO: unregisterIOWatch(*watchPoint, IO_Out); break; case WatchPoint::READ_MEM: case WatchPoint::WRITE_MEM: updateMemWatch(type); break; default: UNREACHABLE; break; } break; } } } const MSXCPUInterface::WatchPoints& MSXCPUInterface::getWatchPoints() const { return watchPoints; } void MSXCPUInterface::setCondition(const shared_ptr& cond) { conditions.push_back(cond); } void MSXCPUInterface::removeCondition(const DebugCondition& cond) { for (auto it = conditions.begin(); it != conditions.end(); ++it) { if (it->get() == &cond) { conditions.erase(it); break; } } } const MSXCPUInterface::Conditions& MSXCPUInterface::getConditions() { return conditions; } void MSXCPUInterface::registerIOWatch(WatchPoint& watchPoint, MSXDevice** devices) { assert(dynamic_cast(&watchPoint)); auto& ioWatch = static_cast(watchPoint); unsigned beginPort = ioWatch.getBeginAddress(); unsigned endPort = ioWatch.getEndAddress(); assert(beginPort <= endPort); assert(endPort < 0x100); for (unsigned port = beginPort; port <= endPort; ++port) { ioWatch.getDevice(port).getDevicePtr() = devices[port]; devices[port] = &ioWatch.getDevice(port); } } void MSXCPUInterface::unregisterIOWatch(WatchPoint& watchPoint, MSXDevice** devices) { assert(dynamic_cast(&watchPoint)); auto& ioWatch = static_cast(watchPoint); unsigned beginPort = ioWatch.getBeginAddress(); unsigned endPort = ioWatch.getEndAddress(); assert(beginPort <= endPort); assert(endPort < 0x100); for (unsigned port = beginPort; port <= endPort; ++port) { // find pointer to watchpoint MSXDevice** prev = &devices[port]; while (*prev != &ioWatch.getDevice(port)) { prev = &checked_cast(*prev)->getDevicePtr(); } // remove watchpoint from chain *prev = checked_cast(*prev)->getDevicePtr(); } } void MSXCPUInterface::updateMemWatch(WatchPoint::Type type) { std::bitset* watchSet = (type == WatchPoint::READ_MEM) ? readWatchSet : writeWatchSet; for (unsigned i = 0; i < CacheLine::NUM; ++i) { watchSet[i].reset(); } for (auto& w : watchPoints) { if (w->getType() == type) { unsigned beginAddr = w->getBeginAddress(); unsigned endAddr = w->getEndAddress(); assert(beginAddr <= endAddr); assert(endAddr < 0x10000); for (unsigned addr = beginAddr; addr <= endAddr; ++addr) { watchSet[addr >> CacheLine::BITS].set( addr & CacheLine::LOW); } } } for (unsigned i = 0; i < CacheLine::NUM; ++i) { if (readWatchSet [i].any()) { disallowReadCache [i] |= MEMORY_WATCH_BIT; } else { disallowReadCache [i] &= ~MEMORY_WATCH_BIT; } if (writeWatchSet[i].any()) { disallowWriteCache[i] |= MEMORY_WATCH_BIT; } else { disallowWriteCache[i] &= ~MEMORY_WATCH_BIT; } } msxcpu.invalidateMemCache(0x0000, 0x10000); } void MSXCPUInterface::executeMemWatch(WatchPoint::Type type, unsigned address, unsigned value) { assert(!watchPoints.empty()); if (isFastForward()) return; Tcl_Interp* interp = watchPoints.front()->getInterpreter(); Tcl_SetVar(interp, "wp_last_address", StringOp::toString(address).c_str(), TCL_GLOBAL_ONLY); if (value != ~0u) { Tcl_SetVar(interp, "wp_last_value", StringOp::toString(value).c_str(), TCL_GLOBAL_ONLY); } auto wpCopy = watchPoints; for (auto& w : wpCopy) { if ((w->getBeginAddress() <= address) && (w->getEndAddress() >= address) && (w->getType() == type)) { w->checkAndExecute(); } } Tcl_UnsetVar(interp, "wp_last_address", TCL_GLOBAL_ONLY); Tcl_UnsetVar(interp, "wp_last_value", TCL_GLOBAL_ONLY); } void MSXCPUInterface::doBreak() { assert(!isFastForward()); if (breaked) return; breaked = true; msxcpu.exitCPULoopSync(); Reactor& reactor = motherBoard.getReactor(); reactor.block(); breakedSetting->setReadOnlyValue("true"); reactor.getCliComm().update(CliComm::STATUS, "cpu", "suspended"); reactor.getEventDistributor().distributeEvent( std::make_shared(OPENMSX_BREAK_EVENT)); } void MSXCPUInterface::doStep() { assert(!isFastForward()); if (breaked) { step = true; doContinue2(); } } void MSXCPUInterface::doContinue() { assert(!isFastForward()); if (breaked) { continued = true; doContinue2(); } } void MSXCPUInterface::doContinue2() { breaked = false; Reactor& reactor = motherBoard.getReactor(); breakedSetting->setReadOnlyValue("false"); reactor.getCliComm().update(CliComm::STATUS, "cpu", "running"); reactor.unblock(); } void MSXCPUInterface::cleanup() { // before the Tcl interpreter is destroyed, we must delete all // TclObjects. Breakpoints and conditions contain such objects // for the condition and action. // TODO it would be nicer if breakpoints and conditions were not // global objects. breakPoints.clear(); conditions.clear(); } // class MemoryDebug MemoryDebug::MemoryDebug( MSXCPUInterface& interface_, MSXMotherBoard& motherBoard) : SimpleDebuggable(motherBoard, "memory", "The memory currently visible for the CPU.", 0x10000) , interface(interface_) { } byte MemoryDebug::read(unsigned address, EmuTime::param time) { return interface.peekMem(address, time); } void MemoryDebug::write(unsigned address, byte value, EmuTime::param time) { return interface.writeMem(address, value, time); } // class SlottedMemoryDebug SlottedMemoryDebug::SlottedMemoryDebug( MSXCPUInterface& interface_, MSXMotherBoard& motherBoard) : SimpleDebuggable(motherBoard, "slotted memory", "The memory in slots and subslots.", 0x10000 * 4 * 4) , interface(interface_) { } byte SlottedMemoryDebug::read(unsigned address, EmuTime::param time) { return interface.peekSlottedMem(address, time); } void SlottedMemoryDebug::write(unsigned address, byte value, EmuTime::param time) { return interface.writeSlottedMem(address, value, time); } // class SubSlottedInfo static unsigned getSlot(const TclObject& token, const string& itemName) { unsigned slot = token.getInt(); if (slot >= 4) { throw CommandException(itemName + " must be in range 0..3"); } return slot; } SlotInfo::SlotInfo(InfoCommand& machineInfoCommand, MSXCPUInterface& interface_) : InfoTopic(machineInfoCommand, "slot") , interface(interface_) { } void SlotInfo::execute(const vector& tokens, TclObject& result) const { if (tokens.size() != 5) { throw SyntaxError(); } unsigned ps = getSlot(tokens[2], "Primary slot"); unsigned ss = getSlot(tokens[3], "Secondary slot"); unsigned page = getSlot(tokens[4], "Page"); if (!interface.isExpanded(ps)) { ss = 0; } interface.slotLayout[ps][ss][page]->getNameList(result); } string SlotInfo::help(const vector& /*tokens*/) const { return "Retrieve name of the device inserted in given " "primary slot / secondary slot / page."; } // class SubSlottedInfo SubSlottedInfo::SubSlottedInfo(InfoCommand& machineInfoCommand, MSXCPUInterface& interface_) : InfoTopic(machineInfoCommand, "issubslotted") , interface(interface_) { } void SubSlottedInfo::execute(const vector& tokens, TclObject& result) const { if (tokens.size() != 3) { throw SyntaxError(); } result.setInt(interface.isExpanded(getSlot(tokens[2], "Slot"))); } string SubSlottedInfo::help( const vector& /*tokens*/) const { return "Indicates whether a certain primary slot is expanded."; } // class ExternalSlotInfo ExternalSlotInfo::ExternalSlotInfo(InfoCommand& machineInfoCommand, CartridgeSlotManager& manager_) : InfoTopic(machineInfoCommand, "isexternalslot") , manager(manager_) { } void ExternalSlotInfo::execute(const vector& tokens, TclObject& result) const { int ps = 0; int ss = 0; switch (tokens.size()) { case 4: ss = getSlot(tokens[3], "Secondary slot"); // Fall-through case 3: ps = getSlot(tokens[2], "Primary slot"); break; default: throw SyntaxError(); } result.setInt(manager.isExternalSlot(ps, ss, true)); } string ExternalSlotInfo::help( const vector& /*tokens*/) const { return "Indicates whether a certain slot is external or internal."; } // class IODebug IODebug::IODebug(MSXCPUInterface& interface_, MSXMotherBoard& motherBoard) : SimpleDebuggable(motherBoard, "ioports", "IO ports.", 0x100) , interface(interface_) { } byte IODebug::read(unsigned address, EmuTime::param time) { return interface.IO_In[address & 0xFF]->peekIO(address, time); } void IODebug::write(unsigned address, byte value, EmuTime::param time) { interface.writeIO(word(address), value, time); } // class IOInfo IOInfo::IOInfo(InfoCommand& machineInfoCommand, MSXCPUInterface& interface_, bool input_) : InfoTopic(machineInfoCommand, input_ ? "input_port" : "output_port") , interface(interface_), input(input_) { } void IOInfo::execute(const vector& tokens, TclObject& result) const { if (tokens.size() != 3) { throw SyntaxError(); } unsigned port = tokens[2].getInt(); if (port >= 256) { throw CommandException("Port must be in range 0..255"); } MSXDevice** devices = input ? interface.IO_In : interface.IO_Out; result.setString(devices[port]->getName()); } string IOInfo::help(const vector& /*tokens*/) const { return "Return the name of the device connected to the given IO port."; } template void MSXCPUInterface::serialize(Archive& ar, unsigned /*version*/) { // TODO watchPoints ??? // primary and 4 secundary slot select registers byte prim = 0; if (!ar.isLoader()) { for (int i = 0; i < 4; ++i) { prim |= primarySlotState[i] << (2 * i); } } ar.serialize("primarySlots", prim); ar.serialize("subSlotRegs", subSlotRegister); if (ar.isLoader()) { setPrimarySlots(prim); for (int i = 0; i < 4; ++i) { setSubSlot(i, subSlotRegister[i]); } } if (delayDevice) { ar.serialize("vdpDelay", *delayDevice); } } INSTANTIATE_SERIALIZE_METHODS(MSXCPUInterface); } // namespace openmsx openmsx-0.10.0/src/cpu/BreakPointBase.hh0000644000175000017500000000207512262345041020600 0ustar manuelmanuel00000000000000#ifndef BREAKPOINTBASE_HH #define BREAKPOINTBASE_HH #include "TclObject.hh" #include "noncopyable.hh" #include "string_ref.hh" #include struct Tcl_Interp; namespace openmsx { class GlobalCliComm; /** Base class for CPU break and watch points. */ class BreakPointBase : private noncopyable { public: string_ref getCondition() const { return condition.getString(); } string_ref getCommand() const { return command .getString(); } TclObject getConditionObj() const { return condition; } TclObject getCommandObj() const { return command; } void checkAndExecute(); // get associated interpreter Tcl_Interp* getInterpreter() const; protected: // Note: we require GlobalCliComm here because breakpoint objects can // be transfered to different MSX machines, and so the MSXCliComm // object won't remain valid. BreakPointBase(GlobalCliComm& cliComm, TclObject command, TclObject condition); private: bool isTrue() const; GlobalCliComm& cliComm; TclObject command; TclObject condition; bool executing; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/IRQHelper.hh0000644000175000017500000000416412262345041017543 0ustar manuelmanuel00000000000000#ifndef IRQHELPER_HH #define IRQHELPER_HH #include "Probe.hh" #include "noncopyable.hh" #include namespace openmsx { class MSXMotherBoard; class MSXCPU; /** Helper class for doing interrupt request (IRQ) administration. * IRQ is either enabled or disabled; when enabled it contributes * one to the CPU IRQ count, when disabled zero. * Calling set() in enabled state does nothing; * neither does calling reset() in disabled state. */ // policy class for IRQ source class IRQSource { protected: explicit IRQSource(MSXCPU& cpu); void raise(); void lower(); private: MSXCPU& cpu; }; // policy class for NMI source class NMISource { protected: explicit NMISource(MSXCPU& cpu); void raise(); void lower(); private: MSXCPU& cpu; }; // policy class that can dynamically switch between IRQ and NMI class DynamicSource { public: enum IntType { IRQ, NMI }; void setIntType(IntType type_) { type = type_; } protected: explicit DynamicSource(MSXCPU& cpu); void raise(); void lower(); private: MSXCPU& cpu; IntType type; }; // generic implementation template class IntHelper : public SOURCE, private noncopyable { public: /** Create a new IntHelper. * Initially there is no interrupt request on the bus. */ explicit IntHelper(MSXMotherBoard& motherboard, const std::string& name); /** Destroy this IntHelper. * Resets interrupt request if it is active. */ ~IntHelper() { reset(); } /** Set the interrupt request on the bus. */ inline void set() { if (!request) { request = true; SOURCE::raise(); } } /** Reset the interrupt request on the bus. */ inline void reset() { if (request) { request = false; SOURCE::lower(); } } /** Get the interrupt state. * @return true iff interrupt request is active. */ inline bool getState() const { return request; } template void serialize(Archive& ar, unsigned /*version*/); private: Probe request; }; // convenience types typedef IntHelper IRQHelper; //typedef IntHelper NMIHelper; //typedef IntHelper IRQNMIHelper; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/WatchPoint.hh0000644000175000017500000000176712262345041020036 0ustar manuelmanuel00000000000000#ifndef WATCHPOINT_HH #define WATCHPOINT_HH #include "BreakPointBase.hh" namespace openmsx { /** Base class for CPU breakpoints. * For performance reasons every bp is associated with exactly one * (immutable) address. */ class WatchPoint : public BreakPointBase { public: enum Type { READ_IO, WRITE_IO, READ_MEM, WRITE_MEM }; /** Begin and end address are inclusive (IOW range = [begin, end]) */ WatchPoint(GlobalCliComm& CliComm, TclObject command, TclObject condition, Type type, unsigned beginAddr, unsigned endAddr, unsigned newId = -1); virtual ~WatchPoint(); // needed for dynamic_cast unsigned getId() const { return id; } Type getType() const { return type; } unsigned getBeginAddress() const { return beginAddr; } unsigned getEndAddress() const { return endAddr; } private: const unsigned id; const unsigned beginAddr; const unsigned endAddr; const Type type; static unsigned lastId; }; } // namespace openmsx #endif openmsx-0.10.0/src/cpu/CPURegs.hh0000644000175000017500000002755012262345041017224 0ustar manuelmanuel00000000000000#ifndef CPUREGS_HH #define CPUREGS_HH #include "serialize_meta.hh" #include "openmsx.hh" #include "build-info.hh" #include namespace openmsx { template struct z80regpair_8bit; template<> struct z80regpair_8bit { byte l, h; }; template<> struct z80regpair_8bit { byte h, l; }; typedef union { z80regpair_8bit b; word w; } z80regpair; class CPURegs { public: CPURegs(bool r800) : HALT_(0), Rmask(r800 ? 0xff : 0x7f) {} inline byte getA() const { return AF_.b.h; } inline byte getF() const { return AF_.b.l; } inline byte getB() const { return BC_.b.h; } inline byte getC() const { return BC_.b.l; } inline byte getD() const { return DE_.b.h; } inline byte getE() const { return DE_.b.l; } inline byte getH() const { return HL_.b.h; } inline byte getL() const { return HL_.b.l; } inline byte getA2() const { return AF2_.b.h; } inline byte getF2() const { return AF2_.b.l; } inline byte getB2() const { return BC2_.b.h; } inline byte getC2() const { return BC2_.b.l; } inline byte getD2() const { return DE2_.b.h; } inline byte getE2() const { return DE2_.b.l; } inline byte getH2() const { return HL2_.b.h; } inline byte getL2() const { return HL2_.b.l; } inline byte getIXh() const { return IX_.b.h; } inline byte getIXl() const { return IX_.b.l; } inline byte getIYh() const { return IY_.b.h; } inline byte getIYl() const { return IY_.b.l; } inline byte getPCh() const { return PC_.b.h; } inline byte getPCl() const { return PC_.b.l; } inline byte getSPh() const { return SP_.b.h; } inline byte getSPl() const { return SP_.b.l; } inline unsigned getAF() const { return AF_.w; } inline unsigned getBC() const { return BC_.w; } inline unsigned getDE() const { return DE_.w; } inline unsigned getHL() const { return HL_.w; } inline unsigned getAF2() const { return AF2_.w; } inline unsigned getBC2() const { return BC2_.w; } inline unsigned getDE2() const { return DE2_.w; } inline unsigned getHL2() const { return HL2_.w; } inline unsigned getIX() const { return IX_.w; } inline unsigned getIY() const { return IY_.w; } inline unsigned getPC() const { return PC_.w; } inline unsigned getSP() const { return SP_.w; } inline byte getIM() const { return IM_; } inline byte getI() const { return I_; } inline byte getR() const { return (R_ & Rmask) | (R2_ & ~Rmask); } inline bool getIFF1() const { return IFF1_; } inline bool getIFF2() const { return IFF2_; } inline byte getHALT() const { return HALT_; } inline void setA(byte x) { AF_.b.h = x; } inline void setF(byte x) { AF_.b.l = x; } inline void setB(byte x) { BC_.b.h = x; } inline void setC(byte x) { BC_.b.l = x; } inline void setD(byte x) { DE_.b.h = x; } inline void setE(byte x) { DE_.b.l = x; } inline void setH(byte x) { HL_.b.h = x; } inline void setL(byte x) { HL_.b.l = x; } inline void setA2(byte x) { AF2_.b.h = x; } inline void setF2(byte x) { AF2_.b.l = x; } inline void setB2(byte x) { BC2_.b.h = x; } inline void setC2(byte x) { BC2_.b.l = x; } inline void setD2(byte x) { DE2_.b.h = x; } inline void setE2(byte x) { DE2_.b.l = x; } inline void setH2(byte x) { HL2_.b.h = x; } inline void setL2(byte x) { HL2_.b.l = x; } inline void setIXh(byte x) { IX_.b.h = x; } inline void setIXl(byte x) { IX_.b.l = x; } inline void setIYh(byte x) { IY_.b.h = x; } inline void setIYl(byte x) { IY_.b.l = x; } inline void setPCh(byte x) { PC_.b.h = x; } inline void setPCl(byte x) { PC_.b.l = x; } inline void setSPh(byte x) { SP_.b.h = x; } inline void setSPl(byte x) { SP_.b.l = x; } inline void setAF(unsigned x) { AF_.w = x; } inline void setBC(unsigned x) { BC_.w = x; } inline void setDE(unsigned x) { DE_.w = x; } inline void setHL(unsigned x) { HL_.w = x; } inline void setAF2(unsigned x) { AF2_.w = x; } inline void setBC2(unsigned x) { BC2_.w = x; } inline void setDE2(unsigned x) { DE2_.w = x; } inline void setHL2(unsigned x) { HL2_.w = x; } inline void setIX(unsigned x) { IX_.w = x; } inline void setIY(unsigned x) { IY_.w = x; } inline void setPC(unsigned x) { PC_.w = x; } inline void setSP(unsigned x) { SP_.w = x; } inline void setIM(byte x) { IM_ = x; } inline void setI(byte x) { I_ = x; } inline void setR(byte x) { R_ = x; R2_ = x; } inline void setIFF1(bool x) { IFF1_ = x; } inline void setIFF2(bool x) { IFF2_ = x; } inline void setHALT(bool x) { HALT_ = (HALT_ & ~1) | (x ? 1 : 0); } inline void setExtHALT(bool x) { HALT_ = (HALT_ & ~2) | (x ? 2 : 0); } inline void incR(byte x) { R_ += x; } // These methods are used to set/query whether the previously // executed instruction was a 'EI' or 'LD A,{I,R}' instruction. // Initially this could only be queried between two // instructions, so e.g. after the EI instruction was executed // but before the next one has started, for emulation this is // good enough. But for debugging we still want to be able to // query this info during the execution of the next // instruction: e.g. a IO-watchpoint is triggered during the // execution of some OUT instruction, at the time we evaluate // the condition for that watchpoint, we still want to be able // to query whether the previous instruction was a EI // instruction. inline bool isSameAfter() const { // Between two instructions these two should be the same return after_ == afterNext_; } inline bool getAfterEI() const { assert(isSameAfter()); return (after_ & 0x01) != 0; } inline bool getAfterLDAI() const { assert(isSameAfter()); return (after_ & 0x02) != 0; } inline bool debugGetAfterEI() const { // Can be called during execution of an instruction return (after_ & 0x01) != 0; } inline void clearNextAfter() { // Right before executing an instruction this should be // cleared afterNext_ = 0x00; } inline bool isNextAfterClear() const { // In the fast code path we avoid calling clearNextAfter() // before every instruction. But in debug mode we want // to verify that this optimzation is valid. return afterNext_ == 0; } inline void setAfterEI() { // Set both after_ and afterNext_. Can only be called // at the end of an instruction (status of prev // instruction can't be queried anymore) assert(isNextAfterClear()); afterNext_ = after_ = 0x01; } inline void setAfterLDAI() { // Set both, see above. assert(isNextAfterClear()); afterNext_ = after_ = 0x02; } inline void copyNextAfter() { // At the end of an instruction, the next flags become // the current flags. setAfterEI/LDAI() already sets // both after_ and afterNext_, thus calling this method // is only required to clear the flags. Instructions // right after a EI or LD A,I/R instruction are always // executed in the slow code path. So this means that // in the fast code path we don't need to call // copyNextAfter(). after_ = afterNext_; } template void serialize(Archive& ar, unsigned version); private: z80regpair PC_; z80regpair AF_, BC_, DE_, HL_; z80regpair AF2_, BC2_, DE2_, HL2_; z80regpair IX_, IY_, SP_; bool IFF1_, IFF2_; byte after_, afterNext_; byte HALT_; byte IM_, I_; byte R_, R2_; // refresh = R & Rmask | R2 & ~Rmask const byte Rmask; // 0x7F for Z80, 0xFF for R800 }; SERIALIZE_CLASS_VERSION(CPURegs, 2); /* The above implementation uses a union to access the upper/lower 8 bits in a * 16 bit value. At some point in the past I replaced this with the * implementation below because it generated faster code. Though when I measure * it now, the original version is faster again. * TODO need to investigate this further. */ #if 0 class CPURegs { public: inline byte getA() const { return AF >> 8; } inline byte getF() const { return AF & 255; } inline byte getB() const { return BC >> 8; } inline byte getC() const { return BC & 255; } inline byte getD() const { return DE >> 8; } inline byte getE() const { return DE & 255; } inline byte getH() const { return HL >> 8; } inline byte getL() const { return HL & 255; } inline byte getA2() const { return AF2 >> 8; } inline byte getF2() const { return AF2 & 255; } inline byte getB2() const { return BC2 >> 8; } inline byte getC2() const { return BC2 & 255; } inline byte getD2() const { return DE2 >> 8; } inline byte getE2() const { return DE2 & 255; } inline byte getH2() const { return HL2 >> 8; } inline byte getL2() const { return HL2 & 255; } inline byte getIXh() const { return IX >> 8; } inline byte getIXl() const { return IX & 255; } inline byte getIYh() const { return IY >> 8; } inline byte getIYl() const { return IY & 255; } inline byte getPCh() const { return PC >> 8; } inline byte getPCl() const { return PC & 255; } inline byte getSPh() const { return SP >> 8; } inline byte getSPl() const { return SP & 255; } inline word getAF() const { return AF; } inline word getBC() const { return BC; } inline word getDE() const { return DE; } inline word getHL() const { return HL; } inline word getAF2() const { return AF2; } inline word getBC2() const { return BC2; } inline word getDE2() const { return DE2; } inline word getHL2() const { return HL2; } inline word getIX() const { return IX; } inline word getIY() const { return IY; } inline word getPC() const { return PC; } inline word getSP() const { return SP; } inline byte getIM() const { return IM; } inline byte getI() const { return I; } inline byte getR() const { return (R & 0x7F) | (R2 & 0x80); } inline bool getIFF1() const { return IFF1; } inline bool getIFF2() const { return IFF2; } inline bool getHALT() const { return HALT; } inline void setA(byte x) { AF = (AF & 0x00FF) | (x << 8); } inline void setF(byte x) { AF = (AF & 0xFF00) | x; } inline void setB(byte x) { BC = (BC & 0x00FF) | (x << 8); } inline void setC(byte x) { BC = (BC & 0xFF00) | x; } inline void setD(byte x) { DE = (DE & 0x00FF) | (x << 8); } inline void setE(byte x) { DE = (DE & 0xFF00) | x; } inline void setH(byte x) { HL = (HL & 0x00FF) | (x << 8); } inline void setL(byte x) { HL = (HL & 0xFF00) | x; } inline void setA2(byte x) { AF2 = (AF2 & 0x00FF) | (x << 8); } inline void setF2(byte x) { AF2 = (AF2 & 0xFF00) | x; } inline void setB2(byte x) { BC2 = (BC2 & 0x00FF) | (x << 8); } inline void setC2(byte x) { BC2 = (BC2 & 0xFF00) | x; } inline void setD2(byte x) { DE2 = (DE2 & 0x00FF) | (x << 8); } inline void setE2(byte x) { DE2 = (DE2 & 0xFF00) | x; } inline void setH2(byte x) { HL2 = (HL2 & 0x00FF) | (x << 8); } inline void setL2(byte x) { HL2 = (HL2 & 0xFF00) | x; } inline void setIXh(byte x) { IX = (IX & 0x00FF) | (x << 8); } inline void setIXl(byte x) { IX = (IX & 0xFF00) | x; } inline void setIYh(byte x) { IY = (IY & 0x00FF) | (x << 8); } inline void setIYl(byte x) { IY = (IY & 0xFF00) | x; } inline void setPCh(byte x) { PC = (PC & 0x00FF) | (x << 8); } inline void setPCl(byte x) { PC = (PC & 0xFF00) | x; } inline void setSPh(byte x) { SP = (SP & 0x00FF) | (x << 8); } inline void setSPl(byte x) { SP = (SP & 0xFF00) | x; } inline void setAF(word x) { AF = x; } inline void setBC(word x) { BC = x; } inline void setDE(word x) { DE = x; } inline void setHL(word x) { HL = x; } inline void setAF2(word x) { AF2 = x; } inline void setBC2(word x) { BC2 = x; } inline void setDE2(word x) { DE2 = x; } inline void setHL2(word x) { HL2 = x; } inline void setIX(word x) { IX = x; } inline void setIY(word x) { IY = x; } inline void setPC(word x) { PC = x; } inline void setSP(word x) { SP = x; } inline void setIM(byte x) { IM = x; } inline void setI(byte x) { I = x; } inline void setR(byte x) { R = x; R2 = x; } inline void setIFF1(bool x) { IFF1 = x; } inline void setIFF2(bool x) { IFF2 = x; } inline void setHALT(bool x) { HALT = x; } inline void incR(byte x) { R += x; } private: word AF, BC, DE, HL; word AF2, BC2, DE2, HL2; word IX, IY, PC, SP; bool IFF1, IFF2, HALT; byte IM, I; byte R, R2; // refresh = R&127 | R2&128 }; #endif } // namespace openmsx #endif openmsx-0.10.0/src/cpu/MSXMultiIODevice.cc0000644000175000017500000000340012262345041020760 0ustar manuelmanuel00000000000000#include "MSXMultiIODevice.hh" #include "TclObject.hh" #include "StringOp.hh" #include "xrange.hh" #include #include namespace openmsx { MSXMultiIODevice::MSXMultiIODevice(const HardwareConfig& hwConf) : MSXMultiDevice(hwConf) { } MSXMultiIODevice::~MSXMultiIODevice() { assert(devices.empty()); } void MSXMultiIODevice::addDevice(MSXDevice* device) { assert(std::count(devices.begin(), devices.end(), device) == 0); devices.push_back(device); } void MSXMultiIODevice::removeDevice(MSXDevice* device) { assert(std::count(devices.begin(), devices.end(), device) == 1); devices.erase(std::find(devices.begin(), devices.end(), device)); } MSXMultiIODevice::Devices& MSXMultiIODevice::getDevices() { return devices; } std::string MSXMultiIODevice::getName() const { TclObject list; getNameList(list); return list.getString().str(); } void MSXMultiIODevice::getNameList(TclObject& result) const { for (auto* dev : devices) { const auto& name = dev->getName(); if (!name.empty()) { result.addListElement(name); } } } byte MSXMultiIODevice::readIO(word port, EmuTime::param time) { // conflict: return the result from the first device, call readIO() // also on all other devices, but discard result assert(!devices.empty()); auto it = devices.begin(); byte result = (*it)->readIO(port, time); for (++it; it != devices.end(); ++it) { (*it)->readIO(port, time); } return result; } byte MSXMultiIODevice::peekIO(word port, EmuTime::param time) const { // conflict: just peek first device assert(!devices.empty()); return devices.front()->peekIO(port, time); } void MSXMultiIODevice::writeIO(word port, byte value, EmuTime::param time) { for (auto& d : devices) { d->writeIO(port, value, time); } } } // namespace openmsx openmsx-0.10.0/src/cpu/DebugCondition.cc0000644000175000017500000000046212262345041020630 0ustar manuelmanuel00000000000000#include "DebugCondition.hh" namespace openmsx { unsigned DebugCondition::lastId = 0; DebugCondition::DebugCondition(GlobalCliComm& cliComm, TclObject command, TclObject condition) : BreakPointBase(cliComm, command, condition) , id(++lastId) { } } // namespace openmsx openmsx-0.10.0/src/I8255.hh0000644000175000017500000000310512262345041015667 0ustar manuelmanuel00000000000000// This class implements the Intel 8255 chip // // * Only the 8255 is emulated, no surrounding hardware. // Use the class I8255Interface to do that. // * Only mode 0 (basic input/output) is implemented #ifndef I8255_HH #define I8255_HH #include "EmuTime.hh" #include "openmsx.hh" #include "noncopyable.hh" namespace openmsx { class I8255Interface; class CliComm; class I8255 : private noncopyable { public: I8255(I8255Interface& interf, EmuTime::param time, CliComm& cliComm); void reset(EmuTime::param time); byte readPortA(EmuTime::param time); byte readPortB(EmuTime::param time); byte readPortC(EmuTime::param time); byte readControlPort(EmuTime::param time) const; byte peekPortA(EmuTime::param time) const; byte peekPortB(EmuTime::param time) const; byte peekPortC(EmuTime::param time) const; void writePortA(byte value, EmuTime::param time); void writePortB(byte value, EmuTime::param time); void writePortC(byte value, EmuTime::param time); void writeControlPort(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte readC0(EmuTime::param time); byte readC1(EmuTime::param time); byte peekC0(EmuTime::param time) const; byte peekC1(EmuTime::param time) const; void outputPortA(byte value, EmuTime::param time); void outputPortB(byte value, EmuTime::param time); void outputPortC(byte value, EmuTime::param time); I8255Interface& interface; CliComm& cliComm; byte control; byte latchPortA; byte latchPortB; byte latchPortC; bool warningPrinted; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/0000755000175000017500000000000012262345041015772 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/input/NinjaTap.cc0000644000175000017500000000352412262345041020011 0ustar manuelmanuel00000000000000#include "NinjaTap.hh" #include "JoystickPort.hh" #include "serialize.hh" namespace openmsx { NinjaTap::NinjaTap(PluggingController& pluggingController, const std::string& name) : JoyTap(pluggingController, name) { status = 0x3F; // TODO check initial value previous = 0; for (int i = 0; i < 4; ++i) { buf[i] = 0xFF; } } string_ref NinjaTap::getDescription() const { return "MSX Ninja Tap device"; } void NinjaTap::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { createPorts(pluggingController, "Ninja Tap port "); } byte NinjaTap::read(EmuTime::param /*time*/) { return status; } void NinjaTap::write(byte value, EmuTime::param time) { // bit 0 -> pin 6 // bit 1 -> pin 7 // bit 2 -> pin 8 if (value & 2) { // pin7 = 1 : read mode if (!(value & 1) && (previous & 1)) { // pin 6 1->0 : query joysticks // TODO does output change? for (int i = 0; i < 4; ++i) { byte t = slaves[i]->read(time); buf[i] = ((t & 0x0F) << 4) | ((t & 0x30) >> 4) | 0x0C; } } if (!(value & 4) && (previous & 4)) { // pin 8 1->0 : shift values // TODO what about b4 and b5? byte t = 0; for (int i = 0; i < 4; ++i) { if (buf[i] & 1) t |= (1 << i); buf[i] >>= 1; } status = (status & ~0x0F) | t; } } else { // pin 7 = 0 : detect mode, b5 is inverse of pin8 // TODO what happens with other bits? if (value & 4) { status &= ~0x20; } else { status |= 0x20; } } previous = value; } template void NinjaTap::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("status", status); ar.serialize("previous", previous); ar.serialize("buf", buf); } INSTANTIATE_SERIALIZE_METHODS(NinjaTap); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, NinjaTap, "NinjaTap"); } // namespace openmsx openmsx-0.10.0/src/input/SETetrisDongle.hh0000644000175000017500000000121312262345041021143 0ustar manuelmanuel00000000000000#ifndef SETETRISDONGLE_HH #define SETETRISDONGLE_HH #include "JoystickDevice.hh" namespace openmsx { class SETetrisDongle : public JoystickDevice { public: SETetrisDongle(); // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte status; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/Joystick.hh0000644000175000017500000000446212262345041020120 0ustar manuelmanuel00000000000000#ifndef JOYSTICK_HH #define JOYSTICK_HH #include "JoystickDevice.hh" #include "MSXEventListener.hh" #include "StateChangeListener.hh" #include "serialize_meta.hh" #include #include namespace openmsx { class MSXEventDistributor; class StateChangeDistributor; class CommandController; class PluggingController; class StringSetting; class TclObject; /** Uses an SDL joystick to emulate an MSX joystick. */ class Joystick #ifndef SDL_JOYSTICK_DISABLED : public JoystickDevice, private MSXEventListener, private StateChangeListener #endif { public: /** Register all available SDL joysticks. */ static void registerAll(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, CommandController& commandController, PluggingController& controller); Joystick(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, CommandController& commandController, SDL_Joystick* joystick); virtual ~Joystick(); #ifndef SDL_JOYSTICK_DISABLED // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: void plugHelper2(); byte calcState(); bool getState(const TclObject& dict, string_ref key); void createEvent(EmuTime::param time, byte newStatus); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); MSXEventDistributor& eventDistributor; StateChangeDistributor& stateChangeDistributor; std::unique_ptr configSetting; SDL_Joystick* const joystick; const unsigned joyNum; const std::string name; const std::string desc; byte status; bool pin8; #endif // SDL_JOYSTICK_DISABLED }; SERIALIZE_CLASS_VERSION(Joystick, 2); } // namespace openmsx #endif openmsx-0.10.0/src/input/Mouse.hh0000644000175000017500000000335012262345041017404 0ustar manuelmanuel00000000000000#ifndef MOUSE_HH #define MOUSE_HH #include "JoystickDevice.hh" #include "MSXEventListener.hh" #include "StateChangeListener.hh" #include "serialize_meta.hh" namespace openmsx { class MSXEventDistributor; class StateChangeDistributor; class Mouse : public JoystickDevice, private MSXEventListener , private StateChangeListener { public: Mouse(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor); virtual ~Mouse(); template void serialize(Archive& ar, unsigned version); private: // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); void createMouseStateChange(EmuTime::param time, int deltaX, int deltaY, byte press, byte release); void emulateJoystick(); void plugHelper2(); MSXEventDistributor& eventDistributor; StateChangeDistributor& stateChangeDistributor; EmuTime lastTime; int phase; int xrel, yrel; // latched X/Y values, these are returned to the MSX int curxrel, curyrel; // running X/Y values, already scaled down int absHostX, absHostY; // running X/Y values, not yet scaled down byte status; bool mouseMode; }; SERIALIZE_CLASS_VERSION(Mouse, 4); } // namespace openmsx #endif openmsx-0.10.0/src/input/RecordedCommand.hh0000644000175000017500000000710312262345041021342 0ustar manuelmanuel00000000000000#ifndef RECORDEDCOMMAND_HH #define RECORDEDCOMMAND_HH #include "Command.hh" #include "StateChangeListener.hh" #include "StateChange.hh" #include "TclObject.hh" #include "EmuTime.hh" #include namespace openmsx { class CommandController; class StateChangeDistributor; class Scheduler; /** This class is used to for Tcl commands that directly influence the MSX * state (e.g. plug, disk, cassetteplayer, reset). It's passed via an * event because the recording needs to see these. */ class MSXCommandEvent : public StateChange { public: MSXCommandEvent() {} // for serialize MSXCommandEvent(const std::vector& tokens, EmuTime::param time); MSXCommandEvent(const std::vector& tokens, EmuTime::param time); virtual ~MSXCommandEvent(); const std::vector& getTokens() const; template void serialize(Archive& ar, unsigned version); private: std::vector tokens; }; /** Commands that directly influence the MSX state should send and events * so that they can be recorded by the event recorder. This class helps to * implement that. * * Note: when a recorded command is later replayed, it's important to check * whether it's actually a recorded command and not some arbitrary other * command that someone might have inserted in a replay file. IOW when you * load a replay file from an untrusted source, it should never be able to * cause any harm. Blindly executing any Tcl command would be bad. */ class RecordedCommand : public Command, private StateChangeListener { public: /** This is like the execute() method of the Command class, it only * has an extra time parameter. * There are two variants of this method. The one with tclObjects is * the fastest, the one with strings is often more convenient to use. * Subclasses must reimplement exactly one of these two. */ virtual void execute( const std::vector& tokens, TclObject& result, EmuTime::param time); virtual std::string execute( const std::vector& tokens, EmuTime::param time); /** It's possible that in some cases the command doesn't need to be * recorded after all (e.g. a query subcommand). In that case you can * override this method. Return false iff the command doesn't need * to be recorded. * Similar to the execute() method above there are two variants of * this method. However in this case it's allowed to override none * or just one of the two variants (but not both). * The default implementation always returns true (will always * record). If this default implementation is fine but if speed is * very important (e.g. the debug command) it is still recommenced * to override the TclObject variant of this method (and just return * true). */ virtual bool needRecord(const std::vector& tokens) const; virtual bool needRecord(const std::vector& tokens) const; protected: RecordedCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, string_ref name); virtual ~RecordedCommand(); private: // Command virtual void execute(const std::vector& tokens, TclObject& result); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); StateChangeDistributor& stateChangeDistributor; Scheduler& scheduler; const std::unique_ptr dummyResultObject; TclObject* currentResultObject; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/StateChangeDistributor.cc0000644000175000017500000000475412262345041022734 0ustar manuelmanuel00000000000000#include "StateChangeDistributor.hh" #include "StateChangeListener.hh" #include "StateChange.hh" #include #include namespace openmsx { StateChangeDistributor::StateChangeDistributor() : recorder(nullptr) , viewOnlyMode(false) { } StateChangeDistributor::~StateChangeDistributor() { assert(listeners.empty()); } bool StateChangeDistributor::isRegistered(StateChangeListener* listener) const { return find(listeners.begin(), listeners.end(), listener) != listeners.end(); } void StateChangeDistributor::registerListener(StateChangeListener& listener) { assert(!isRegistered(&listener)); listeners.push_back(&listener); } void StateChangeDistributor::unregisterListener(StateChangeListener& listener) { assert(isRegistered(&listener)); listeners.erase(find(listeners.begin(), listeners.end(), &listener)); } void StateChangeDistributor::registerRecorder(StateChangeRecorder& recorder_) { assert(!recorder); recorder = &recorder_; } void StateChangeDistributor::unregisterRecorder(StateChangeRecorder& recorder_) { (void)recorder_; assert(recorder == &recorder_); recorder = nullptr; } void StateChangeDistributor::distributeNew(const EventPtr& event) { if (viewOnlyMode && isReplaying()) return; if (isReplaying()) { stopReplay(event->getTime()); } distribute(event); } void StateChangeDistributor::distributeReplay(const EventPtr& event) { assert(isReplaying()); distribute(event); } void StateChangeDistributor::distribute(const EventPtr& event) { // Iterate over a copy because signalStateChange() may indirect call // back into registerListener(). // e.g. signalStateChange() -> .. -> PlugCmd::execute() -> .. -> // Connector::plug() -> .. -> Joystick::plugHelper() -> // registerListener() if (recorder) recorder->signalStateChange(event); auto copy = listeners; for (auto& l : copy) { if (isRegistered(l)) { // it's possible the listener unregistered itself // (but is still present in the copy) l->signalStateChange(event); } } } void StateChangeDistributor::stopReplay(EmuTime::param time) { if (!isReplaying()) return; if (recorder) recorder->stopReplay(time); for (auto& l : listeners) { l->stopReplay(time); } } void StateChangeDistributor::setViewOnlyMode(bool value) { viewOnlyMode = value; } bool StateChangeDistributor::isViewOnlyMode() const { return viewOnlyMode; } bool StateChangeDistributor::isReplaying() const { if (recorder) { return recorder->isReplaying(); } return false; } } // namespace openmsx openmsx-0.10.0/src/input/Mouse.cc0000644000175000017500000002262312262345041017376 0ustar manuelmanuel00000000000000#include "Mouse.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "InputEvents.hh" #include "StateChange.hh" #include "Clock.hh" #include "checked_cast.hh" #include "serialize.hh" #include "serialize_meta.hh" #include "unreachable.hh" using std::string; using std::min; using std::max; using std::shared_ptr; namespace openmsx { static const int TRESHOLD = 2; static const int SCALE = 2; static const int PHASE_XHIGH = 0; static const int PHASE_XLOW = 1; static const int PHASE_YHIGH = 2; static const int PHASE_YLOW = 3; static const int STROBE = 0x04; class MouseState: public StateChange { public: MouseState() {} // for serialize MouseState(EmuTime::param time, int deltaX_, int deltaY_, byte press_, byte release_) : StateChange(time) , deltaX(deltaX_), deltaY(deltaY_) , press(press_), release(release_) {} int getDeltaX() const { return deltaX; } int getDeltaY() const { return deltaY; } byte getPress() const { return press; } byte getRelease() const { return release; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("deltaX", deltaX); ar.serialize("deltaY", deltaY); ar.serialize("press", press); ar.serialize("release", release); } private: int deltaX, deltaY; byte press, release; }; REGISTER_POLYMORPHIC_CLASS(StateChange, MouseState, "MouseState"); Mouse::Mouse(MSXEventDistributor& eventDistributor_, StateChangeDistributor& stateChangeDistributor_) : eventDistributor(eventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , lastTime(EmuTime::zero) { status = JOY_BUTTONA | JOY_BUTTONB; phase = PHASE_YLOW; xrel = yrel = curxrel = curyrel = 0; absHostX = absHostY = 0; mouseMode = true; } Mouse::~Mouse() { if (isPluggedIn()) { Mouse::unplugHelper(EmuTime::dummy()); } } // Pluggable const string& Mouse::getName() const { static const string name("mouse"); return name; } string_ref Mouse::getDescription() const { return "MSX mouse"; } void Mouse::plugHelper(Connector& /*connector*/, EmuTime::param time) { if (status & JOY_BUTTONA) { // not pressed, mouse mode mouseMode = true; lastTime = time; } else { // left mouse button pressed, joystick emulation mode mouseMode = false; } plugHelper2(); } void Mouse::plugHelper2() { eventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); } void Mouse::unplugHelper(EmuTime::param /*time*/) { stateChangeDistributor.unregisterListener(*this); eventDistributor.unregisterEventListener(*this); } // JoystickDevice byte Mouse::read(EmuTime::param /*time*/) { if (mouseMode) { switch (phase) { case PHASE_XHIGH: return ((xrel >> 4) & 0x0F) | status; case PHASE_XLOW: return (xrel & 0x0F) | status; case PHASE_YHIGH: return ((yrel >> 4) & 0x0F) | status; case PHASE_YLOW: return (yrel & 0x0F) | status; default: UNREACHABLE; return 0; } } else { emulateJoystick(); return status; } } void Mouse::emulateJoystick() { status &= ~(JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT); int deltax = curxrel; curxrel = 0; int deltay = curyrel; curyrel = 0; int absx = (deltax > 0) ? deltax : -deltax; int absy = (deltay > 0) ? deltay : -deltay; if ((absx < TRESHOLD) && (absy < TRESHOLD)) { return; } // tan(pi/8) ~= 5/12 if (deltax > 0) { if (deltay > 0) { if ((12 * absx) > (5 * absy)) { status |= JOY_RIGHT; } if ((12 * absy) > (5 * absx)) { status |= JOY_DOWN; } } else { if ((12 * absx) > (5 * absy)) { status |= JOY_RIGHT; } if ((12 * absy) > (5 * absx)) { status |= JOY_UP; } } } else { if (deltay > 0) { if ((12 * absx) > (5 * absy)) { status |= JOY_LEFT; } if ((12 * absy) > (5 * absx)) { status |= JOY_DOWN; } } else { if ((12 * absx) > (5 * absy)) { status |= JOY_LEFT; } if ((12 * absy) > (5 * absx)) { status |= JOY_UP; } } } } void Mouse::write(byte value, EmuTime::param time) { if (mouseMode) { // TODO figure out the exact timeout value. Is there even such // an exact value or can it vary between different mouse // models? // // Initially we used a timeout of 1 full second. This caused bug // [3520394] Mouse behaves badly (unusable) in HiBrid // Slightly lowering the value to around 0.94s was already // enough to fix that bug. Later we found that to make FRS's // joytest program work we need a value that is less than the // duration of one (NTSC) frame. See bug // #474 Mouse doesn't work properly on Joytest v2.2 // We still don't know the exact value that an actual MSX mouse // uses, but 1.5ms is also the timeout value that is used for // JoyMega, so it seems like a reasonable value. if ((time - lastTime) > EmuDuration::usec(1500)) { phase = PHASE_YLOW; } lastTime = time; switch (phase) { case PHASE_XHIGH: if ((value & STROBE) == 0) phase = PHASE_XLOW; break; case PHASE_XLOW: if ((value & STROBE) != 0) phase = PHASE_YHIGH; break; case PHASE_YHIGH: if ((value & STROBE) == 0) phase = PHASE_YLOW; break; case PHASE_YLOW: if ((value & STROBE) != 0) { phase = PHASE_XHIGH; xrel = curxrel; yrel = curyrel; curxrel = 0; curyrel = 0; } break; } } else { // ignore } } // MSXEventListener void Mouse::signalEvent(const shared_ptr& event, EmuTime::param time) { switch (event->getType()) { case OPENMSX_MOUSE_MOTION_EVENT: { auto& mev = checked_cast(*event); if (mev.getX() || mev.getY()) { // note: X/Y are negated, do this already in this // routine to keep replays bw-compat. In a new // savestate version it may (or may not) be cleaner // to perform this operation closer to the MSX code. createMouseStateChange(time, -mev.getX(), -mev.getY(), 0, 0); } break; } case OPENMSX_MOUSE_BUTTON_DOWN_EVENT: { auto& butEv = checked_cast(*event); switch (butEv.getButton()) { case MouseButtonEvent::LEFT: createMouseStateChange(time, 0, 0, JOY_BUTTONA, 0); break; case MouseButtonEvent::RIGHT: createMouseStateChange(time, 0, 0, JOY_BUTTONB, 0); break; default: // ignore other buttons break; } break; } case OPENMSX_MOUSE_BUTTON_UP_EVENT: { auto& butEv = checked_cast(*event); switch (butEv.getButton()) { case MouseButtonEvent::LEFT: createMouseStateChange(time, 0, 0, 0, JOY_BUTTONA); break; case MouseButtonEvent::RIGHT: createMouseStateChange(time, 0, 0, 0, JOY_BUTTONB); break; default: // ignore other buttons break; } break; } default: // ignore break; } } void Mouse::createMouseStateChange( EmuTime::param time, int deltaX, int deltaY, byte press, byte release) { stateChangeDistributor.distributeNew(std::make_shared( time, deltaX, deltaY, press, release)); } void Mouse::signalStateChange(const shared_ptr& event) { auto ms = dynamic_cast(event.get()); if (!ms) return; // This is almost the same as // relMsxXY = ms->getDeltaXY() / SCALE // except that it doesn't accumulate rounding errors int oldMsxX = absHostX / SCALE; int oldMsxY = absHostY / SCALE; absHostX += ms->getDeltaX(); absHostY += ms->getDeltaY(); int newMsxX = absHostX / SCALE; int newMsxY = absHostY / SCALE; int relMsxX = newMsxX - oldMsxX; int relMsxY = newMsxY - oldMsxY; // Verified with a real MSX-mouse (Philips SBC3810): // this value is not clipped to -128 .. 127. curxrel += relMsxX; curyrel += relMsxY; status = (status & ~ms->getPress()) | ms->getRelease(); } void Mouse::stopReplay(EmuTime::param time) { // TODO read actual host mouse button state int dx = 0 - curxrel; int dy = 0 - curyrel; byte release = (JOY_BUTTONA | JOY_BUTTONB) & ~status; if ((dx != 0) || (dy != 0) || (release != 0)) { createMouseStateChange(time, dx, dy, 0, release); } } // version 1: Initial version, the variables curxrel, curyrel and status were // not serialized. // version 2: Also serialize the above variables, this is required for // record/replay, see comment in Keyboard.cc for more details. // version 3: variables '(cur){x,y}rel' are scaled to MSX coordinates // version 4: simplified type of 'lastTime' from Clock<> to EmuTime template void Mouse::serialize(Archive& ar, unsigned version) { if (ar.isLoader() && isPluggedIn()) { // Do this early, because if something goes wrong while loading // some state below, then unplugHelper() gets called and that // will assert when plugHelper2() wasn't called yet. plugHelper2(); } if (ar.versionBelow(version, 4)) { assert(ar.isLoader()); Clock<1000> tmp(EmuTime::zero); ar.serialize("lastTime", tmp); lastTime = tmp.getTime(); } else { ar.serialize("lastTime", lastTime); } ar.serialize("faze", phase); // TODO fix spelling if there's ever a need // to bump the serialization verion ar.serialize("xrel", xrel); ar.serialize("yrel", yrel); ar.serialize("mouseMode", mouseMode); if (ar.versionAtLeast(version, 2)) { ar.serialize("curxrel", curxrel); ar.serialize("curyrel", curyrel); ar.serialize("status", status); } if (ar.versionBelow(version, 3)) { xrel /= SCALE; yrel /= SCALE; curxrel /= SCALE; curyrel /= SCALE; } // no need to serialize absHostX,Y } INSTANTIATE_SERIALIZE_METHODS(Mouse); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, Mouse, "Mouse"); } // namespace openmsx openmsx-0.10.0/src/input/KeyJoystick.hh0000644000175000017500000000351612262345041020570 0ustar manuelmanuel00000000000000#ifndef KEYJOYSTICK_HH #define KEYJOYSTICK_HH #include "JoystickDevice.hh" #include "MSXEventListener.hh" #include "StateChangeListener.hh" #include "serialize_meta.hh" #include namespace openmsx { class CommandController; class KeyCodeSetting; class MSXEventDistributor; class StateChangeDistributor; class KeyJoystick : public JoystickDevice, private MSXEventListener , private StateChangeListener { public: KeyJoystick(CommandController& commandController, MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, const std::string& name); virtual ~KeyJoystick(); template void serialize(Archive& ar, unsigned version); private: // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // KeyJoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); MSXEventDistributor& eventDistributor; StateChangeDistributor& stateChangeDistributor; const std::string name; const std::unique_ptr up; const std::unique_ptr down; const std::unique_ptr left; const std::unique_ptr right; const std::unique_ptr trigA; const std::unique_ptr trigB; byte status; bool pin8; }; SERIALIZE_CLASS_VERSION(KeyJoystick, 2); } // namespace openmsx #endif openmsx-0.10.0/src/input/EventDelay.hh0000644000175000017500000000305412262345041020355 0ustar manuelmanuel00000000000000#ifndef EVENTDELAY_HH #define EVENTDELAY_HH #include "EventListener.hh" #include "Schedulable.hh" #include "EmuTime.hh" #include "Keys.hh" #include "build-info.hh" #include #include #include #include #include namespace openmsx { class Scheduler; class CommandController; class Event; class EventDistributor; class MSXEventDistributor; class ReverseManager; class FloatSetting; /** This class is responsible for translating host events into MSX events. * It also translates host event timing into EmuTime. To better do this * we introduce a small delay (default 0.03s) in this translation. */ class EventDelay : private EventListener, private Schedulable { public: EventDelay(Scheduler& scheduler, CommandController& commandController, EventDistributor& eventDistributor, MSXEventDistributor& msxEventDistributor, ReverseManager& reverseManager); virtual ~EventDelay(); void sync(EmuTime::param time); void flush(); private: typedef std::shared_ptr EventPtr; // EventListener virtual int signalEvent(const EventPtr& event); // Schedulable virtual void executeUntil(EmuTime::param time, int userData); EventDistributor& eventDistributor; MSXEventDistributor& msxEventDistributor; std::vector toBeScheduledEvents; std::deque scheduledEvents; #if PLATFORM_ANDROID std::map nonMatchedKeyPresses; #endif EmuTime prevEmu; uint64_t prevReal; const std::unique_ptr delaySetting; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/node.mk0000644000175000017500000000070612262345041017253 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ Keyboard \ KeyboardSettings \ UnicodeKeymap \ JoystickPort \ JoystickDevice \ DummyJoystick \ Joystick JoyMega \ KeyJoystick \ SETetrisDongle \ MagicKey \ Mouse \ Trackball \ Touchpad \ JoyTap \ NinjaTap \ ArkanoidPad \ EventDelay \ MSXEventDistributor \ StateChangeDistributor \ RecordedCommand HDR_ONLY:= \ MSXEventListener \ StateChangeListener \ StateChange include build/node-end.mk openmsx-0.10.0/src/input/JoyMega.cc0000644000175000017500000002302612262345041017637 0ustar manuelmanuel00000000000000#include "JoyMega.hh" #include "PluggingController.hh" #include "PlugException.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "InputEvents.hh" #include "InputEventGenerator.hh" #include "StateChange.hh" #include "checked_cast.hh" #include "serialize.hh" #include "serialize_meta.hh" #include "memory.hh" #include "unreachable.hh" #include "build-info.hh" using std::string; using std::shared_ptr; namespace openmsx { #if PLATFORM_ANDROID static const int THRESHOLD = 32768 / 4; #else static const int THRESHOLD = 32768 / 10; #endif void JoyMega::registerAll(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, PluggingController& controller) { #ifdef SDL_JOYSTICK_DISABLED (void)eventDistributor; (void)stateChangeDistributor; (void)controller; #else if (!SDL_WasInit(SDL_INIT_JOYSTICK)) { SDL_InitSubSystem(SDL_INIT_JOYSTICK); SDL_JoystickEventState(SDL_ENABLE); // joysticks generate events } unsigned numJoysticks = SDL_NumJoysticks(); for (unsigned i = 0; i < numJoysticks; i++) { if (SDL_Joystick* joystick = SDL_JoystickOpen(i)) { // Avoid devices that have axes but no buttons, like accelerometers. // SDL 1.2.14 in Linux has an issue where it rejects a device from // /dev/input/event* if it has no buttons but does not reject a // device from /dev/input/js* if it has no buttons, while // accelerometers do end up being symlinked as a joystick in // practice. if (InputEventGenerator::joystickNumButtons(joystick) != 0) { controller.registerPluggable( make_unique( eventDistributor, stateChangeDistributor, joystick)); } } } #endif } class JoyMegaState : public StateChange { public: JoyMegaState() {} // for serialize JoyMegaState(EmuTime::param time, unsigned joyNum_, unsigned press_, unsigned release_) : StateChange(time) , joyNum(joyNum_), press(press_), release(release_) { assert((press != 0) || (release != 0)); assert((press & release) == 0); } unsigned getJoystick() const { return joyNum; } unsigned getPress() const { return press; } unsigned getRelease() const { return release; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("joyNum", joyNum); ar.serialize("press", press); ar.serialize("release", release); } private: unsigned joyNum; unsigned press, release; }; REGISTER_POLYMORPHIC_CLASS(StateChange, JoyMegaState, "JoyMegaState"); #ifndef SDL_JOYSTICK_DISABLED // Note: It's OK to open/close the same SDL_Joystick multiple times (we open it // once per MSX machine). The SDL documentation doesn't state this, but I // checked the implementation and a SDL_Joystick uses a 'reference count' on // the open/close calls. JoyMega::JoyMega(MSXEventDistributor& eventDistributor_, StateChangeDistributor& stateChangeDistributor_, SDL_Joystick* joystick_) : eventDistributor(eventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , joystick(joystick_) , joyNum(SDL_JoystickIndex(joystick_)) , name("joymegaX") // 'X' is filled in below , desc(string(SDL_JoystickName(joyNum))) , lastTime(EmuTime::zero) { const_cast(name)[7] = char('1' + joyNum); } JoyMega::~JoyMega() { if (isPluggedIn()) { JoyMega::unplugHelper(EmuTime::dummy()); } SDL_JoystickClose(joystick); } // Pluggable const string& JoyMega::getName() const { return name; } string_ref JoyMega::getDescription() const { return desc; } void JoyMega::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { plugHelper2(); status = calcInitialState(); cycle = 0; // when mode button is pressed when joystick is plugged in, then // act as a 3-button joypad (otherwise 6-button) cycleMask = (status & 0x800) ? 7 : 1; } void JoyMega::plugHelper2() { eventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); } void JoyMega::unplugHelper(EmuTime::param /*time*/) { stateChangeDistributor.unregisterListener(*this); eventDistributor.unregisterEventListener(*this); } // JoystickDevice byte JoyMega::read(EmuTime::param time) { // See http://segaretro.org/Control_Pad_(Mega_Drive) // and http://frs.badcoffee.info/hardware/joymega-en.html // for a detailed description of the MegaDrive joystick. checkTime(time); switch (cycle) { case 0: case 2: case 4: // up/down/left/right/b/c return (status & 0x00f) | ((status & 0x060) >> 1); case 1: case 3: // up/down/0/0/a/start return (status & 0x013) | ((status & 0x080) >> 2); case 5: // 0/0/0/0/a/start return (status & 0x010) | ((status & 0x080) >> 2); case 6: // z/y/x/mode/b/c return ((status & 0x400) >> 10) | // z ((status & 0xA00) >> 8) | // start+y ((status & 0x100) >> 6) | // x ((status & 0x060) >> 1); // c+b case 7: // 1/1/1/1/a/start return (status & 0x010) | ((status & 0x080) >> 2) | 0x0f; default: UNREACHABLE; return 0; } } void JoyMega::write(byte value, EmuTime::param time) { checkTime(time); lastTime = time; if (((value >> 2) & 1) != (cycle & 1)) { cycle = (cycle + 1) & cycleMask; } assert(((value >> 2) & 1) == (cycle & 1)); } void JoyMega::checkTime(EmuTime::param time) { if ((time - lastTime) > EmuDuration::usec(1500)) { // longer than 1.5ms since last write -> reset cycle cycle = 0; } } static unsigned encodeButton(unsigned button, byte cycleMask) { unsigned n = (cycleMask == 7) ? 7 : 3; // 6- or 3-button mode return 1 << (4 + (button & n)); } unsigned JoyMega::calcInitialState() { unsigned result = 0xfff; int xAxis = SDL_JoystickGetAxis(joystick, 0); if (xAxis < -THRESHOLD) { result &= ~JOY_LEFT; } else if (xAxis > THRESHOLD) { result &= ~JOY_RIGHT; } int yAxis = SDL_JoystickGetAxis(joystick, 1); if (yAxis < -THRESHOLD) { result &= ~JOY_UP; } else if (yAxis > THRESHOLD) { result &= ~JOY_DOWN; } int numButtons = InputEventGenerator::joystickNumButtons(joystick); for (int button = 0; button < numButtons; ++button) { if (InputEventGenerator::joystickGetButton(joystick, button)) { result &= ~encodeButton(button, 7); } } return result; } // MSXEventListener void JoyMega::signalEvent(const shared_ptr& event, EmuTime::param time) { auto joyEvent = dynamic_cast(event.get()); if (!joyEvent) return; // TODO: It would be more efficient to make a dispatcher instead of // sending the event to all joysticks. if (joyEvent->getJoystick() != joyNum) return; switch (event->getType()) { case OPENMSX_JOY_AXIS_MOTION_EVENT: { auto& mev = checked_cast(*event); short value = mev.getValue(); switch (mev.getAxis() & 1) { case JoystickAxisMotionEvent::X_AXIS: // Horizontal if (value < -THRESHOLD) { // left, not right createEvent(time, JOY_LEFT, JOY_RIGHT); } else if (value > THRESHOLD) { // not left, right createEvent(time, JOY_RIGHT, JOY_LEFT); } else { // not left, not right createEvent(time, 0, JOY_LEFT | JOY_RIGHT); } break; case JoystickAxisMotionEvent::Y_AXIS: // Vertical if (value < -THRESHOLD) { // up, not down createEvent(time, JOY_UP, JOY_DOWN); } else if (value > THRESHOLD) { // not up, down createEvent(time, JOY_DOWN, JOY_UP); } else { // not up, not down createEvent(time, 0, JOY_UP | JOY_DOWN); } break; default: // ignore other axis break; } break; } case OPENMSX_JOY_BUTTON_DOWN_EVENT: { auto& butEv = checked_cast(*event); createEvent(time, encodeButton(butEv.getButton(), cycleMask), 0); break; } case OPENMSX_JOY_BUTTON_UP_EVENT: { auto& butEv = checked_cast(*event); createEvent(time, 0, encodeButton(butEv.getButton(), cycleMask)); break; } default: UNREACHABLE; } } void JoyMega::createEvent(EmuTime::param time, unsigned press, unsigned release) { unsigned newStatus = (status & ~press) | release; createEvent(time, newStatus); } void JoyMega::createEvent(EmuTime::param time, unsigned newStatus) { unsigned diff = status ^ newStatus; if (!diff) { // event won't actually change the status, so ignore it return; } // make sure we create an event with minimal changes unsigned press = status & diff; unsigned release = newStatus & diff; stateChangeDistributor.distributeNew(std::make_shared( time, joyNum, press, release)); } // StateChangeListener void JoyMega::signalStateChange(const shared_ptr& event) { auto js = dynamic_cast(event.get()); if (!js) return; // TODO: It would be more efficient to make a dispatcher instead of // sending the event to all joysticks. // TODO an alternative is to log events based on the connector instead // of the joystick. That would make it possible to replay on a // different host without an actual SDL joystick connected. if (js->getJoystick() != joyNum) return; status = (status & ~js->getPress()) | js->getRelease(); } void JoyMega::stopReplay(EmuTime::param time) { createEvent(time, calcInitialState()); } template void JoyMega::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("lastTime", lastTime); ar.serialize("status", status); ar.serialize("cycle", cycle); ar.serialize("cycleMask", cycleMask); if (ar.isLoader()) { if (isPluggedIn()) { plugHelper2(); } } } INSTANTIATE_SERIALIZE_METHODS(JoyMega); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, JoyMega, "JoyMega"); #endif // SDL_JOYSTICK_DISABLED } // namespace openmsx openmsx-0.10.0/src/input/JoystickDevice.cc0000644000175000017500000000022412262345041021216 0ustar manuelmanuel00000000000000#include "JoystickDevice.hh" namespace openmsx { string_ref JoystickDevice::getClass() const { return "Joystick Port"; } } // namespace openmsx openmsx-0.10.0/src/input/StateChangeListener.hh0000644000175000017500000000214212262345041022206 0ustar manuelmanuel00000000000000#ifndef STATECHANGELISTENER_HH #define STATECHANGELISTENER_HH #include "EmuTime.hh" #include namespace openmsx { class StateChange; class StateChangeListener { public: /** This method gets called when a StateChange event occurs. * This can be either a replayed or a 'live' event, (though that * shouldn't matter, it should be handled in exactly the same way). */ virtual void signalStateChange( const std::shared_ptr& event) = 0; /** This method gets called when we switch from replayed events to * live events. A input device should resync its state with the current * host state. A replayer/recorder should switch from replay to record. * Note that it's not possible to switch back to replay state (when * the user triggers a replay, we always start from a snapshot, so * we create 'fresh' objects). */ virtual void stopReplay(EmuTime::param time) = 0; protected: StateChangeListener() {} virtual ~StateChangeListener() {} }; class StateChangeRecorder : public StateChangeListener { public: virtual bool isReplaying() const = 0; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/Joystick.cc0000644000175000017500000002421112262345041020100 0ustar manuelmanuel00000000000000#include "Joystick.hh" #include "PluggingController.hh" #include "PlugException.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "InputEvents.hh" #include "InputEventGenerator.hh" #include "StateChange.hh" #include "TclObject.hh" #include "StringSetting.hh" #include "CommandException.hh" #include "checked_cast.hh" #include "serialize.hh" #include "serialize_meta.hh" #include "memory.hh" #include "unreachable.hh" #include "xrange.hh" #include "build-info.hh" using std::string; using std::shared_ptr; namespace openmsx { #if PLATFORM_ANDROID static const int THRESHOLD = 32768 / 4; #else static const int THRESHOLD = 32768 / 10; #endif void Joystick::registerAll(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, CommandController& commandController, PluggingController& controller) { #ifdef SDL_JOYSTICK_DISABLED (void)eventDistributor; (void)stateChangeDistributor; (void)controller; #else if (!SDL_WasInit(SDL_INIT_JOYSTICK)) { SDL_InitSubSystem(SDL_INIT_JOYSTICK); SDL_JoystickEventState(SDL_ENABLE); // joysticks generate events } unsigned numJoysticks = SDL_NumJoysticks(); ad_printf("#joysticks: %d\n", numJoysticks); for (unsigned i = 0; i < numJoysticks; i++) { SDL_Joystick* joystick = SDL_JoystickOpen(i); if (joystick) { // Avoid devices that have axes but no buttons, like accelerometers. // SDL 1.2.14 in Linux has an issue where it rejects a device from // /dev/input/event* if it has no buttons but does not reject a // device from /dev/input/js* if it has no buttons, while // accelerometers do end up being symlinked as a joystick in // practice. if (InputEventGenerator::joystickNumButtons(joystick) != 0) { controller.registerPluggable( make_unique( eventDistributor, stateChangeDistributor, commandController, joystick)); } } } #endif } class JoyState : public StateChange { public: JoyState() {} // for serialize JoyState(EmuTime::param time, unsigned joyNum_, byte press_, byte release_) : StateChange(time) , joyNum(joyNum_), press(press_), release(release_) { assert((press != 0) || (release != 0)); assert((press & release) == 0); } unsigned getJoystick() const { return joyNum; } byte getPress() const { return press; } byte getRelease() const { return release; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("joyNum", joyNum); ar.serialize("press", press); ar.serialize("release", release); } private: unsigned joyNum; byte press, release; }; REGISTER_POLYMORPHIC_CLASS(StateChange, JoyState, "JoyState"); void checkJoystickConfig(TclObject& newValue) { unsigned n = newValue.getListLength(); if (n & 1) { throw CommandException("Need an even number of elements"); } for (unsigned i = 0; i < n; i += 2) { string_ref key = newValue.getListIndex(i + 0).getString(); TclObject value = newValue.getListIndex(i + 1); if ((key != "A" ) && (key != "B" ) && (key != "LEFT") && (key != "RIGHT") && (key != "UP" ) && (key != "DOWN" )) { throw CommandException( "Invalid MSX joystick action: must be one of " "'A', 'B', 'LEFT', 'RIGHT', 'UP', 'DOWN'."); } for (auto j : xrange(value.getListLength())) { string_ref host = value.getListIndex(j).getString(); if (!host.starts_with("button") && !host.starts_with("+axis") && !host.starts_with("-axis")) { throw CommandException( "Invalid host joystick action: must be " "one of 'button', '+axis', '-axis'"); } } } } #ifndef SDL_JOYSTICK_DISABLED // Note: It's OK to open/close the same SDL_Joystick multiple times (we open it // once per MSX machine). The SDL documentation doesn't state this, but I // checked the implementation and a SDL_Joystick uses a 'reference count' on // the open/close calls. Joystick::Joystick(MSXEventDistributor& eventDistributor_, StateChangeDistributor& stateChangeDistributor_, CommandController& commandController, SDL_Joystick* joystick_) : eventDistributor(eventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , joystick(joystick_) , joyNum(SDL_JoystickIndex(joystick_)) , name("joystickX") // 'X' is filled in below , desc(string(SDL_JoystickName(joyNum))) { const_cast(name)[8] = char('1' + joyNum); // create config setting TclObject value; value.addListElement("LEFT" ); value.addListElement("-axis0"); value.addListElement("RIGHT"); value.addListElement("+axis0"); value.addListElement("UP" ); value.addListElement("-axis1"); value.addListElement("DOWN" ); value.addListElement("+axis1"); TclObject listA, listB; for (auto i : xrange(InputEventGenerator::joystickNumButtons(joystick))) { string button = "button" + StringOp::toString(i); if (i & 1) { listB.addListElement(button); } else { listA.addListElement(button); } } value.addListElement("A"); value.addListElement(listA); value.addListElement("B"); value.addListElement(listB); configSetting = make_unique( commandController, name + "_config", "joystick configuration", value.getString()); configSetting->setChecker(checkJoystickConfig); pin8 = false; // avoid UMR } Joystick::~Joystick() { if (isPluggedIn()) { Joystick::unplugHelper(EmuTime::dummy()); } if (joystick) { SDL_JoystickClose(joystick); } } // Pluggable const string& Joystick::getName() const { return name; } string_ref Joystick::getDescription() const { return desc; } void Joystick::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { if (!joystick) { throw PlugException("Failed to open joystick device"); } plugHelper2(); status = calcState(); } void Joystick::plugHelper2() { eventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); } void Joystick::unplugHelper(EmuTime::param /*time*/) { stateChangeDistributor.unregisterListener(*this); eventDistributor.unregisterEventListener(*this); } // JoystickDevice byte Joystick::read(EmuTime::param /*time*/) { return status; } void Joystick::write(byte value, EmuTime::param /*time*/) { pin8 = (value & 0x04) != 0; } byte Joystick::calcState() { byte result = JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT | JOY_BUTTONA | JOY_BUTTONB; if (joystick && !pin8) { const TclObject& dict = configSetting->getValue(); if (getState(dict, "A" )) result &= ~JOY_BUTTONA; if (getState(dict, "B" )) result &= ~JOY_BUTTONB; if (getState(dict, "UP" )) result &= ~JOY_UP; if (getState(dict, "DOWN" )) result &= ~JOY_DOWN; if (getState(dict, "LEFT" )) result &= ~JOY_LEFT; if (getState(dict, "RIGHT")) result &= ~JOY_RIGHT; } return result; } bool Joystick::getState(const TclObject& dict, string_ref key) { try { const auto& list = dict.getDictValue(TclObject(key)); for (auto i : xrange(list.getListLength())) { const auto& elem = list.getListIndex(i).getString(); if (elem.starts_with("button")) { int n = stoi(elem.substr(6)); if (InputEventGenerator::joystickGetButton(joystick, n)) { return true; } } else if (elem.starts_with("+axis")) { int n = stoi(elem.substr(5)); if (SDL_JoystickGetAxis(joystick, n) > THRESHOLD) { return true; } } else if (elem.starts_with("-axis")) { int n = stoi(elem.substr(5)); if (SDL_JoystickGetAxis(joystick, n) < -THRESHOLD) { return true; } } } } catch (MSXException&) { // ignore } return false; } // MSXEventListener void Joystick::signalEvent(const shared_ptr& event, EmuTime::param time) { auto joyEvent = dynamic_cast(event.get()); if (!joyEvent) return; // TODO: It would be more efficient to make a dispatcher instead of // sending the event to all joysticks. if (joyEvent->getJoystick() != joyNum) return; // TODO: Currently this recalculates the whole joystick state. It might // be possible to implement this more efficiently by using the specific // event information. Though that's not trivial because e.g. multiple // host buttons can map to the same MSX button. Also calcState() // involves some string processing. It might be possible to only parse // the config once (per setting change). Though this solution is likely // good enough. createEvent(time, calcState()); } void Joystick::createEvent(EmuTime::param time, byte newStatus) { byte diff = status ^ newStatus; if (!diff) { // event won't actually change the status, so ignore it return; } // make sure we create an event with minimal changes byte press = status & diff; byte release = newStatus & diff; stateChangeDistributor.distributeNew(std::make_shared( time, joyNum, press, release)); } // StateChangeListener void Joystick::signalStateChange(const shared_ptr& event) { auto js = dynamic_cast(event.get()); if (!js) return; // TODO: It would be more efficient to make a dispatcher instead of // sending the event to all joysticks. // TODO an alternative is to log events based on the connector instead // of the joystick. That would make it possible to replay on a // different host without an actual SDL joystick connected. if (js->getJoystick() != joyNum) return; status = (status & ~js->getPress()) | js->getRelease(); } void Joystick::stopReplay(EmuTime::param time) { createEvent(time, calcState()); } // version 1: Initial version, the variable status was not serialized. // version 2: Also serialize the above variable, this is required for // record/replay, see comment in Keyboard.cc for more details. template void Joystick::serialize(Archive& ar, unsigned version) { if (ar.versionAtLeast(version, 2)) { ar.serialize("status", status); } if (ar.isLoader()) { if (joystick && isPluggedIn()) { plugHelper2(); } } // no need to serialize 'pin8' it's automatically restored via write() } INSTANTIATE_SERIALIZE_METHODS(Joystick); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, Joystick, "Joystick"); #endif // SDL_JOYSTICK_DISABLED } // namespace openmsx openmsx-0.10.0/src/input/Touchpad.cc0000644000175000017500000001773712262345041020067 0ustar manuelmanuel00000000000000// This implementation is based on the reverse-engineering effort done // by 'SD-snatcher'. See here: // http://www.msx.org/news/en/msx-touchpad-protocol-reverse-engineered // http://www.msx.org/wiki/Touchpad // Also thanks to Manuel Bilderbeek for donating a Philips NMS-1150 that // made this effort possible. #include "Touchpad.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "InputEvents.hh" #include "StateChange.hh" #include "StringSetting.hh" #include "CommandException.hh" #include "Clock.hh" #include "Math.hh" #include "checked_cast.hh" #include "serialize.hh" #include "serialize_meta.hh" #include #include using std::shared_ptr; namespace openmsx { class TouchpadState : public StateChange { public: TouchpadState() {} // for serialize TouchpadState(EmuTime::param time, byte x_, byte y_, bool touch_, bool button_) : StateChange(time) , x(x_), y(y_), touch(touch_), button(button_) {} byte getX() const { return x; } byte getY() const { return y; } bool getTouch() const { return touch; } bool getButton() const { return button; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("x", x); ar.serialize("y", y); ar.serialize("touch", touch); ar.serialize("button", button); } private: byte x, y; bool touch, button; }; REGISTER_POLYMORPHIC_CLASS(StateChange, TouchpadState, "TouchpadState"); Touchpad::Touchpad(MSXEventDistributor& eventDistributor_, StateChangeDistributor& stateChangeDistributor_, CommandController& commandController) : eventDistributor(eventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , start(EmuTime::zero) , hostX(0), hostY(0), hostButtons(0) , x(0), y(0), touch(false), button(false) , shift(0), last(0) { transformSetting = make_unique(commandController, "touchpad_transform_matrix", "2x3 matrix to transform host mouse coordinates to " "MSX touchpad coordinates, see manual for details", "{ 256 0 0 } { 0 256 0 }"); transformSetting->setChecker([this](TclObject& newValue) { try { parseTransformMatrix(newValue); } catch (CommandException& e) { throw CommandException( "Invalid transformation matrix: " + e.getMessage()); } }); try { parseTransformMatrix(transformSetting->getValue()); } catch (CommandException& e) { // should only happen when settings.xml was manually edited std::cerr << e.getMessage() << std::endl; // fill in safe default values m[0][0] = 256.0; m[0][1] = 0.0; m[0][2] = 0.0; m[1][0] = 0.0; m[1][1] = 256.0; m[1][2] = 0.0; } } Touchpad::~Touchpad() { if (isPluggedIn()) { Touchpad::unplugHelper(EmuTime::dummy()); } } void Touchpad::parseTransformMatrix(const TclObject& value) { if (value.getListLength() != 2) { throw CommandException("must have 2 rows"); } for (int i = 0; i < 2; ++i) { TclObject row = value.getListIndex(i); if (row.getListLength() != 3) { throw CommandException("each row must have 3 elements"); } for (int j = 0; j < 3; ++j) { m[i][j] = row.getListIndex(j).getDouble(); } } } // Pluggable const std::string& Touchpad::getName() const { static const std::string name("touchpad"); return name; } string_ref Touchpad::getDescription() const { return "MSX Touchpad"; } void Touchpad::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { eventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); } void Touchpad::unplugHelper(EmuTime::param /*time*/) { stateChangeDistributor.unregisterListener(*this); eventDistributor.unregisterEventListener(*this); } // JoystickDevice byte Touchpad::read(EmuTime::param time) { static const byte SENSE = RD_PIN1; static const byte EOC = RD_PIN2; static const byte SO = RD_PIN3; static const byte BUTTON = RD_PIN4; byte result = SENSE | BUTTON; // 1-bit means not pressed if (touch) result &= ~SENSE; if (button) result &= ~BUTTON; // EOC remains zero for 56 cycles after CS 0->1 // TODO at what clock frequency does the UPD7001 run? // 400kHz is only the recommended value from the UPD7001 datasheet. static const EmuDuration delta = Clock<400000>::duration(56); if ((time - start) > delta) { result |= EOC; } if (shift & 0x80) result |= SO; return result; } void Touchpad::write(byte value, EmuTime::param time) { static const byte SCK = WR_PIN6; static const byte SI = WR_PIN7; static const byte CS = WR_PIN8; byte diff = last ^ value; last = value; if (diff & CS) { if (value & CS) { // CS 0->1 channel = shift & 3; start = time; // to keep EOC=0 for 56 cycles } else { // CS 1->0 // Tested by SD-Snatcher (see RFE #252): // When not touched X is always 0, and Y floats // between 147 and 149 (mostly 148). shift = (channel == 0) ? (touch ? x : 0) : (channel == 3) ? (touch ? y : 148) : 0; // channel 1 and 2 return 0 } } if (((value & (CS | SCK)) == SCK) && (diff & SCK)) { // SC=0 & SCK 0->1 shift <<= 1; shift |= (value & SI) != 0; } } void Touchpad::transformCoords(int& x, int& y) { if (SDL_Surface* surf = SDL_GetVideoSurface()) { double u = double(x) / surf->w; double v = double(y) / surf->h; x = m[0][0] * u + m[0][1] * v + m[0][2]; y = m[1][0] * u + m[1][1] * v + m[1][2]; } x = Math::clipIntToByte(x); y = Math::clipIntToByte(y); } // MSXEventListener void Touchpad::signalEvent(const shared_ptr& event, EmuTime::param time) { int x = hostX; int y = hostY; int b = hostButtons; switch (event->getType()) { case OPENMSX_MOUSE_MOTION_EVENT: { auto& mev = checked_cast(*event); x = mev.getAbsX(); y = mev.getAbsY(); transformCoords(x, y); break; } case OPENMSX_MOUSE_BUTTON_DOWN_EVENT: { auto& butEv = checked_cast(*event); switch (butEv.getButton()) { case MouseButtonEvent::LEFT: b |= 1; break; case MouseButtonEvent::RIGHT: b |= 2; break; default: // ignore other buttons break; } break; } case OPENMSX_MOUSE_BUTTON_UP_EVENT: { auto& butEv = checked_cast(*event); switch (butEv.getButton()) { case MouseButtonEvent::LEFT: b &= ~1; break; case MouseButtonEvent::RIGHT: b &= ~2; break; default: // ignore other buttons break; } break; } default: // ignore break; } if ((x != hostX) || (y != hostY) || (b != hostButtons)) { hostX = x; hostY = y; hostButtons = b; createTouchpadStateChange( time, hostX, hostY, (hostButtons & 1) != 0, (hostButtons & 2) != 0); } } void Touchpad::createTouchpadStateChange( EmuTime::param time, byte x, byte y, bool touch, bool button) { stateChangeDistributor.distributeNew(std::make_shared( time, x, y, touch, button)); } // StateChangeListener void Touchpad::signalStateChange(const shared_ptr& event) { if (auto* ts = dynamic_cast(event.get())) { x = ts->getX(); y = ts->getY(); touch = ts->getTouch(); button = ts->getButton(); } } void Touchpad::stopReplay(EmuTime::param time) { // TODO Get actual mouse state. Is it worth the trouble? if (x || y || touch || button) { stateChangeDistributor.distributeNew( std::make_shared( time, 0, 0, false, false)); } } template void Touchpad::serialize(Archive& ar, unsigned /*version*/) { // no need to serialize hostX, hostY, hostButtons, // transformSetting, m[][] ar.serialize("start", start); ar.serialize("x", x); ar.serialize("y", y); ar.serialize("touch", touch); ar.serialize("button", button); ar.serialize("shift", shift); ar.serialize("channel", channel); ar.serialize("last", last); if (ar.isLoader() && isPluggedIn()) { plugHelper(*getConnector(), EmuTime::dummy()); } } INSTANTIATE_SERIALIZE_METHODS(Touchpad); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, Touchpad, "Touchpad"); } // namespace openmsx openmsx-0.10.0/src/input/MSXEventDistributor.hh0000644000175000017500000000234412262345041022222 0ustar manuelmanuel00000000000000#ifndef MSXEVENTDISTRIBUTOR_HH #define MSXEVENTDISTRIBUTOR_HH #include "EmuTime.hh" #include "noncopyable.hh" #include #include namespace openmsx { class MSXEventListener; class Event; class MSXEventDistributor : private noncopyable { public: typedef std::shared_ptr EventPtr; MSXEventDistributor(); ~MSXEventDistributor(); /** * Registers a given object to receive certain events. * @param listener Listener that will be notified when an event arrives. */ void registerEventListener(MSXEventListener& listener); /** * Unregisters a previously registered event listener. * @param listener Listener to unregister. */ void unregisterEventListener(MSXEventListener& listener); /** Deliver the event to all registered listeners * @param event The event * @param time Current time * Note: MSXEventListener's are allowed to throw exceptions, and this * method doesn't catch them (in case of an exception it's * undefined which listeners receive the event) */ void distributeEvent(const EventPtr& event, EmuTime::param time); private: bool isRegistered(MSXEventListener* listener) const; std::vector listeners; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/EventDelay.cc0000644000175000017500000001605112262345041020344 0ustar manuelmanuel00000000000000#include "EventDelay.hh" #include "EventDistributor.hh" #include "MSXEventDistributor.hh" #include "ReverseManager.hh" #include "InputEvents.hh" #include "FloatSetting.hh" #include "Timer.hh" #include "MSXException.hh" #include "checked_cast.hh" #include "memory.hh" #include using std::make_shared; namespace openmsx { EventDelay::EventDelay(Scheduler& scheduler, CommandController& commandController, EventDistributor& eventDistributor_, MSXEventDistributor& msxEventDistributor_, ReverseManager& reverseManager) : Schedulable(scheduler) , eventDistributor(eventDistributor_) , msxEventDistributor(msxEventDistributor_) , prevEmu(EmuTime::zero) , prevReal(Timer::getTime()) , delaySetting(make_unique( commandController, "inputdelay", "delay input to avoid key-skips", 0.0, 0.0, 10.0)) { eventDistributor.registerEventListener( OPENMSX_KEY_DOWN_EVENT, *this, EventDistributor::MSX); eventDistributor.registerEventListener( OPENMSX_KEY_UP_EVENT, *this, EventDistributor::MSX); eventDistributor.registerEventListener( OPENMSX_MOUSE_MOTION_EVENT, *this, EventDistributor::MSX); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this, EventDistributor::MSX); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_UP_EVENT, *this, EventDistributor::MSX); eventDistributor.registerEventListener( OPENMSX_JOY_AXIS_MOTION_EVENT, *this, EventDistributor::MSX); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_DOWN_EVENT, *this, EventDistributor::MSX); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_UP_EVENT, *this, EventDistributor::MSX); reverseManager.registerEventDelay(*this); } EventDelay::~EventDelay() { eventDistributor.unregisterEventListener( OPENMSX_KEY_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_KEY_UP_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_MOTION_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_BUTTON_UP_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_AXIS_MOTION_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_BUTTON_UP_EVENT, *this); } int EventDelay::signalEvent(const EventPtr& event) { toBeScheduledEvents.push_back(event); if (delaySetting->getDouble() == 0.0) { sync(getCurrentTime()); } return 0; } void EventDelay::sync(EmuTime::param curEmu) { auto curRealTime = Timer::getTime(); auto realDuration = curRealTime - prevReal; prevReal = curRealTime; auto emuDuration = curEmu - prevEmu; prevEmu = curEmu; double factor = emuDuration.toDouble() / realDuration; EmuDuration extraDelay(delaySetting->getDouble()); #if PLATFORM_ANDROID // The virtual keyboard on Android sends a key press and the // corresponding key release event directly after each other, without a // delay. It sends both events either when the user has finished a // short tap or alternatively after the user has hold the button // pressed for a few seconds and then has selected the appropriate // character from the multi-character-popup that the virtual keyboard // displays when the user holds a button pressed for a short while. // Either way, the key release event comes way too short after the // press event for the MSX to process it. The two events follow each // other within a few milliseconds at most. Therefore, on Android, // special logic must be foreseen to delay the release event for a // short while. This special logic correlates each key release event // with the corresponding press event for the same key. If they are // less then 2/50 second apart, the release event gets delayed until // the next sync call. The 2/50 second has been chosen because it can // take up to 2 vertical interrupts (2 screen refreshes) for the MSX to // see the key press in the keyboard matrix, thus, 2/50 seconds is the // minimum delay required for an MSX running in PAL mode. std::vector toBeRescheduledEvents; #endif EmuTime time = curEmu + extraDelay; for (auto& e : toBeScheduledEvents) { #if PLATFORM_ANDROID if (e->getType() == OPENMSX_KEY_DOWN_EVENT || e->getType() == OPENMSX_KEY_UP_EVENT) { auto keyEvent = checked_cast(e.get()); int maskedKeyCode = int(keyEvent->getKeyCode()) & int(Keys::K_MASK); if (e->getType() == OPENMSX_KEY_DOWN_EVENT) { nonMatchedKeyPresses[maskedKeyCode] = e; } else { auto nonMatchedKeyPressesIterator = nonMatchedKeyPresses.find(maskedKeyCode); if (nonMatchedKeyPressesIterator != nonMatchedKeyPresses.end()) { auto timedPressEvent = checked_cast(nonMatchedKeyPressesIterator->second.get()); auto timedReleaseEvent = checked_cast(e.get()); auto pressRealTime = timedPressEvent->getRealTime(); auto releaseRealTime = timedReleaseEvent->getRealTime(); auto deltaTime = releaseRealTime - pressRealTime; if (deltaTime <= 2000000 / 50) { // The key release came less then 2 MSX interrupts from the key press. // Reschedule it for the next sync, with the realTime updated to now, so that it seems like the // key was released now and not when android released it. // Otherwise, the offset calculation for the emutime further down below will go wrong on the next sync EventPtr newKeyupEvent = make_shared(keyEvent->getKeyCode(), keyEvent->getUnicode()); toBeRescheduledEvents.push_back(newKeyupEvent); continue; // continue with next to be scheduled event } nonMatchedKeyPresses.erase(nonMatchedKeyPressesIterator); } } } #endif scheduledEvents.push_back(e); auto timedEvent = checked_cast(e.get()); auto eventRealTime = timedEvent->getRealTime(); assert(eventRealTime <= curRealTime); auto offset = curRealTime - eventRealTime; EmuDuration emuOffset(factor * offset); auto schedTime = (emuOffset < extraDelay) ? time - emuOffset : curEmu; assert(curEmu <= schedTime); setSyncPoint(schedTime); } toBeScheduledEvents.clear(); #if PLATFORM_ANDROID toBeScheduledEvents.insert(toBeScheduledEvents.end(), make_move_iterator(toBeRescheduledEvents.begin()), make_move_iterator(toBeRescheduledEvents.end())); #endif } void EventDelay::executeUntil(EmuTime::param time, int /*userData*/) { try { auto event = std::move(scheduledEvents.front()); scheduledEvents.pop_front(); msxEventDistributor.distributeEvent(std::move(event), time); } catch (MSXException&) { // ignore } } void EventDelay::flush() { EmuTime time = getCurrentTime(); for (auto& e : scheduledEvents) { msxEventDistributor.distributeEvent(e, time); } scheduledEvents.clear(); for (auto& e : toBeScheduledEvents) { msxEventDistributor.distributeEvent(e, time); } toBeScheduledEvents.clear(); removeSyncPoints(); } } // namespace openmsx openmsx-0.10.0/src/input/UnicodeKeymap.cc0000644000175000017500000001455612262345041021051 0ustar manuelmanuel00000000000000#include "UnicodeKeymap.hh" #include "MSXException.hh" #include "File.hh" #include "FileContext.hh" #include "FileException.hh" #include "StringOp.hh" #include "stl.hh" #include #include #include #include namespace openmsx { /** Parses the string given by the inclusive begin point and exclusive end * pointer as a hexadecimal integer. * If successful, returns the parsed value and sets "ok" to true. * If unsuccessful, returns 0 and sets "ok" to false. */ static unsigned parseHex(const char* begin, const char* end, bool& ok) { unsigned value = 0; for (; begin != end; begin++) { value *= 16; const char c = *begin; if ('0' <= c && c <= '9') { value += c - '0'; } else if ('A' <= c && c <= 'F') { value += c - 'A' + 10; } else if ('a' <= c && c <= 'f') { value += c - 'a' + 10; } else { ok = false; return 0; } } ok = true; return value; } /** Returns true iff the given character is a separator. * Separators are: comma, whitespace and hash mark. */ static inline bool isSep(char c) { return c == ',' // comma || c == ' ' || c == '\t' || c == '\r' // whitespace || c == '#'; // comment } /** Returns a pointer to the first separator character in the given string, * or a pointer to the end of the line if no separator is found. */ static const char* findSep(const char* begin, const char* end) { while (begin != end && *begin != '\n' && !isSep(*begin)) begin++; return begin; } /** Returns a pointer to the first non-separator character in the given string, * or a pointer to the end of the line if only separators are found. * Characters between a hash mark and the following newline are also skipped. */ static const char* skipSep(const char* begin, const char* end) { while (begin != end) { const char c = *begin; if (!isSep(c)) break; begin++; if (c == '#') { // Skip till end of line. while (begin != end && *begin != '\n') begin++; break; } } return begin; } /** Return true iff the substring [begin, end) equals the given string literal */ template static bool segmentEquals(const char* begin, const char* end, const char (&string)[N]) { return ((end - begin) == (N - 1)) && (strncmp(begin, string, N - 1) == 0); } /** Return true iff the substring [begin, end) starts with the given string literal */ template static bool segmentStartsWith(const char* begin, const char* end, const char (&string)[N]) { return ((end - begin) >= (N - 1)) && (strncmp(begin, string, N - 1) == 0); } UnicodeKeymap::UnicodeKeymap(string_ref keyboardType) : emptyInfo(KeyInfo()) { auto filename = SystemFileContext().resolve( "unicodemaps/unicodemap." + keyboardType); try { File file(filename); size_t size; const byte* buf = file.mmap(size); parseUnicodeKeymapfile( reinterpret_cast(buf), reinterpret_cast(buf + size)); } catch (FileException&) { throw MSXException("Couldn't load unicode keymap file: " + filename); } } UnicodeKeymap::KeyInfo UnicodeKeymap::get(int unicode) const { auto it = lower_bound(mapdata.begin(), mapdata.end(), unicode, LessTupleElement<0>()); return ((it != mapdata.end()) && (it->first == unicode)) ? it->second : emptyInfo; } UnicodeKeymap::KeyInfo UnicodeKeymap::getDeadkey(unsigned n) const { assert(n < NUM_DEAD_KEYS); return deadKeys[n]; } void UnicodeKeymap::parseUnicodeKeymapfile(const char* begin, const char* end) { begin = skipSep(begin, end); while (true) { // Find a line containing tokens. while (begin != end && *begin == '\n') { // Next line. begin = skipSep(begin + 1, end); } if (begin == end) break; // Parse first token: a unicode value or the keyword DEADKEY. const char* tokenEnd = findSep(begin, end); int unicode = 0; unsigned deadKeyIndex = 0; bool isDeadKey = segmentStartsWith(begin, tokenEnd, "DEADKEY"); if (isDeadKey) { const char* begin2 = begin + strlen("DEADKEY"); if (begin2 == tokenEnd) { // The normal keywords are // DEADKEY1 DEADKEY2 DEADKEY3 // but for backwards compatibility also still recognize // DEADKEY } else { bool ok; deadKeyIndex = parseHex(begin2, tokenEnd, ok); deadKeyIndex--; // Make index 0 based instead of 1 based if (!ok || deadKeyIndex >= NUM_DEAD_KEYS) { throw MSXException(StringOp::Builder() << "Wrong deadkey number in keymap file. " "It must be 1.." << NUM_DEAD_KEYS); } } } else { bool ok; unicode = parseHex(begin, tokenEnd, ok); if (!ok || unicode > 0xFFFF) { throw MSXException("Wrong unicode value in keymap file"); } } begin = skipSep(tokenEnd, end); // Parse second token. It must be tokenEnd = findSep(begin, end); if (tokenEnd == end) { throw MSXException("Missing in unicode file"); } bool ok; int rowcol = parseHex(begin, tokenEnd, ok); if (!ok || rowcol >= 0x100) { throw MSXException("Wrong rowcol value in keymap file"); } if ((rowcol >> 4) >= 11) { throw MSXException("Too high row value in keymap file"); } if ((rowcol & 0x0F) >= 8) { throw MSXException("Too high column value in keymap file"); } begin = skipSep(tokenEnd, end); // Parse remaining tokens. It is an optional list of modifier keywords. byte modmask = 0; while (begin != end && *begin != '\n') { tokenEnd = findSep(begin, end); if (segmentEquals(begin, tokenEnd, "SHIFT")) { modmask |= 1; } else if (segmentEquals(begin, tokenEnd, "CTRL")) { modmask |= 2; } else if (segmentEquals(begin, tokenEnd, "GRAPH")) { modmask |= 4; } else if (segmentEquals(begin, tokenEnd, "CAPSLOCK")) { modmask |= 8; } else if (segmentEquals(begin, tokenEnd, "CODE")) { modmask |= 16; } else { throw MSXException(StringOp::Builder() << "Invalid modifier \"" << string_ref(begin, tokenEnd) << "\" in keymap file"); } begin = skipSep(tokenEnd, end); } if (isDeadKey) { deadKeys[deadKeyIndex].row = (rowcol >> 4) & 0x0f; deadKeys[deadKeyIndex].keymask = 1 << (rowcol & 7); deadKeys[deadKeyIndex].modmask = 0; } else { KeyInfo info((rowcol >> 4) & 0x0f, // row 1 << (rowcol & 7), // keymask modmask); // modmask mapdata.push_back(std::make_pair(unicode, info)); } } sort(mapdata.begin(), mapdata.end(), LessTupleElement<0>()); } } // namespace openmsx openmsx-0.10.0/src/input/DummyJoystick.hh0000644000175000017500000000070012262345041021123 0ustar manuelmanuel00000000000000#ifndef DUMMYJOYSTICK_HH #define DUMMYJOYSTICK_HH #include "JoystickDevice.hh" namespace openmsx { class DummyJoystick : public JoystickDevice { public: virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); }; } // namespace openmsx #endif openmsx-0.10.0/src/input/Trackball.hh0000644000175000017500000000270312262345041020214 0ustar manuelmanuel00000000000000#ifndef TRACKBALL_HH #define TRACKBALL_HH #include "JoystickDevice.hh" #include "MSXEventListener.hh" #include "StateChangeListener.hh" namespace openmsx { class MSXEventDistributor; class StateChangeDistributor; class Trackball : public JoystickDevice, private MSXEventListener , private StateChangeListener { public: Trackball(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor); virtual ~Trackball(); template void serialize(Archive& ar, unsigned version); private: void createTrackballStateChange(EmuTime::param time, int deltaX, int deltaY, byte press, byte release); // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); MSXEventDistributor& eventDistributor; StateChangeDistributor& stateChangeDistributor; signed char deltaX, deltaY; byte lastValue; byte status; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/JoystickDevice.hh0000644000175000017500000000315012262345041021231 0ustar manuelmanuel00000000000000#ifndef JOYSTICKDEVICE_HH #define JOYSTICKDEVICE_HH #include "Pluggable.hh" #include "openmsx.hh" namespace openmsx { class JoystickDevice : public Pluggable { public: /** * Read from the joystick device. The bits in the read byte have * following meaning: * 7 6 5 4 3 2 1 0 * | xx | xx | BUTTON_B | BUTTON_A | RIGHT | LEFT | DOWN | UP | * | xx | xx | pin7 | pin6 | pin4 | pin3 | pin2 | pin1| */ virtual byte read(EmuTime::param time) = 0; /** * Write a value to the joystick device. The bits in the written * byte have following meaning: * 7 6 5 4 3 2 1 0 * | xx | xx | xx | xx | xx | pin8 | pin7 | pin6 | * As an optimization, this method might not be called when the * new value is the same as the previous one. */ virtual void write(byte value, EmuTime::param time) = 0; virtual string_ref getClass() const; /* Missing pin descriptions * pin 5 : +5V * pin 9 : GND */ protected: // use in the read() method static const int JOY_UP = 0x01; static const int JOY_DOWN = 0x02; static const int JOY_LEFT = 0x04; static const int JOY_RIGHT = 0x08; static const int JOY_BUTTONA = 0x10; static const int JOY_BUTTONB = 0x20; static const int RD_PIN1 = 0x01; static const int RD_PIN2 = 0x02; static const int RD_PIN3 = 0x04; static const int RD_PIN4 = 0x08; static const int RD_PIN6 = 0x10; static const int RD_PIN7 = 0x20; // use in the write() method static const int WR_PIN6 = 0x01; static const int WR_PIN7 = 0x02; static const int WR_PIN8 = 0x04; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/UnicodeKeymap.hh0000644000175000017500000000157412262345041021057 0ustar manuelmanuel00000000000000#ifndef UNICODEKEYMAP_HH #define UNICODEKEYMAP_HH #include "openmsx.hh" #include "string_ref.hh" #include #include #include namespace openmsx { class UnicodeKeymap { public: struct KeyInfo { KeyInfo(byte row_, byte keymask_, byte modmask_) : row(row_), keymask(keymask_), modmask(modmask_) { if (keymask == 0) { assert(row == 0); assert(modmask == 0); } } KeyInfo() : row(0), keymask(0), modmask(0) { } byte row, keymask, modmask; }; explicit UnicodeKeymap(string_ref keyboardType); KeyInfo get(int unicode) const; KeyInfo getDeadkey(unsigned n) const; private: static const unsigned NUM_DEAD_KEYS = 3; void parseUnicodeKeymapfile(const char* begin, const char* end); std::vector> mapdata; KeyInfo deadKeys[NUM_DEAD_KEYS]; const KeyInfo emptyInfo; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/JoyTap.cc0000644000175000017500000000322612262345041017512 0ustar manuelmanuel00000000000000#include "JoyTap.hh" #include "JoystickPort.hh" #include "StringOp.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { using std::string; JoyTap::JoyTap(PluggingController& pluggingController_, const string& name_) : pluggingController(pluggingController_) , name(name_) { } JoyTap::~JoyTap() { } void JoyTap::createPorts(PluggingController& pluggingController, const string& baseDescription) { for (int i = 0; i < 4; ++i) { slaves[i] = make_unique( pluggingController, StringOp::Builder() << name << "_port_" << char('1' + i), StringOp::Builder() << baseDescription << char('1' + i)); } } string_ref JoyTap::getDescription() const { return "MSX Joy Tap device"; } const std::string& JoyTap::getName() const { return name; } void JoyTap::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { createPorts(pluggingController, "Joy Tap port "); } void JoyTap::unplugHelper(EmuTime::param time) { for (int i = 0; i < 4; ++i) { slaves[i]->unplug(time); slaves[i].reset(); } } byte JoyTap::read(EmuTime::param time) { byte value = 255; for (int i = 0; i < 4; ++i) { value &= slaves[i]->read(time); } return value; } void JoyTap::write(byte value, EmuTime::param time) { for (int i = 0; i < 4; ++i) { slaves[i]->write(value, time); } } template void JoyTap::serialize(Archive& ar, unsigned /*version*/) { char tag[6] = { 'p', 'o', 'r', 't', 'X', 0 }; for (int i = 0; i < 4; ++i) { tag[4] = char('0' + i); ar.serialize(tag, *slaves[i]); } } INSTANTIATE_SERIALIZE_METHODS(JoyTap); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, JoyTap, "JoyTap"); } // namespace openmsx openmsx-0.10.0/src/input/KeyboardSettings.hh0000644000175000017500000000231412262345041021574 0ustar manuelmanuel00000000000000#ifndef KEYBOARDSETTINGS_HH #define KEYBOARDSETTINGS_HH #include "Keys.hh" #include namespace openmsx { class CommandController; class BooleanSetting; template class EnumSetting; class KeyboardSettings { public: enum KpEnterMode { MSX_KP_COMMA, MSX_ENTER }; enum MappingMode { KEY_MAPPING, CHARACTER_MAPPING }; explicit KeyboardSettings(CommandController& commandController); ~KeyboardSettings(); Keys::KeyCode getDeadkeyHostKey(unsigned n) const; EnumSetting& getCodeKanaHostKey() const; EnumSetting& getKpEnterMode() const; EnumSetting& getMappingMode() const; BooleanSetting& getAlwaysEnableKeypad() const; BooleanSetting& getTraceKeyPresses() const; BooleanSetting& getAutoToggleCodeKanaLock() const; private: std::unique_ptr> deadkeyHostKey[3]; std::unique_ptr> codeKanaHostKey; std::unique_ptr> kpEnterMode; std::unique_ptr> mappingMode; std::unique_ptr alwaysEnableKeypad; std::unique_ptr traceKeyPresses; std::unique_ptr autoToggleCodeKanaLock; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/KeyJoystick.cc0000644000175000017500000001326212262345041020555 0ustar manuelmanuel00000000000000#include "KeyJoystick.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "KeyCodeSetting.hh" #include "InputEvents.hh" #include "StateChange.hh" #include "checked_cast.hh" #include "serialize.hh" #include "serialize_meta.hh" #include "memory.hh" #include using std::string; using std::shared_ptr; namespace openmsx { class KeyJoyState : public StateChange { public: KeyJoyState() {} // for serialize KeyJoyState(EmuTime::param time, const string& name_, byte press_, byte release_) : StateChange(time) , name(name_), press(press_), release(release_) {} const string& getName() const { return name; } byte getPress() const { return press; } byte getRelease() const { return release; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("name", name); ar.serialize("press", press); ar.serialize("release", release); } private: string name; byte press, release; }; REGISTER_POLYMORPHIC_CLASS(StateChange, KeyJoyState, "KeyJoyState"); KeyJoystick::KeyJoystick(CommandController& commandController, MSXEventDistributor& eventDistributor_, StateChangeDistributor& stateChangeDistributor_, const string& name_) : eventDistributor(eventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , name(name_) , up (make_unique(commandController, name + ".up", "key for direction up", Keys::K_UP)) , down (make_unique(commandController, name + ".down", "key for direction down", Keys::K_DOWN)) , left (make_unique(commandController, name + ".left", "key for direction left", Keys::K_LEFT)) , right(make_unique(commandController, name + ".right", "key for direction right", Keys::K_RIGHT)) , trigA(make_unique(commandController, name + ".triga", "key for trigger A", Keys::K_SPACE)) , trigB(make_unique(commandController, name + ".trigb", "key for trigger B", Keys::K_M)) { status = JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT | JOY_BUTTONA | JOY_BUTTONB; } KeyJoystick::~KeyJoystick() { if (isPluggedIn()) { KeyJoystick::unplugHelper(EmuTime::dummy()); } } // Pluggable const string& KeyJoystick::getName() const { return name; } string_ref KeyJoystick::getDescription() const { return "Key-Joystick, use your keyboard to emulate an MSX joystick. " "See manual for information on how to configure this."; } void KeyJoystick::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { eventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); } void KeyJoystick::unplugHelper(EmuTime::param /*time*/) { stateChangeDistributor.unregisterListener(*this); eventDistributor.unregisterEventListener(*this); } // KeyJoystickDevice byte KeyJoystick::read(EmuTime::param /*time*/) { return pin8 ? 0x3F : status; } void KeyJoystick::write(byte value, EmuTime::param /*time*/) { pin8 = (value & 0x04) != 0; } // MSXEventListener void KeyJoystick::signalEvent(const shared_ptr& event, EmuTime::param time) { byte press = 0; byte release = 0; switch (event->getType()) { case OPENMSX_KEY_DOWN_EVENT: case OPENMSX_KEY_UP_EVENT: { auto& keyEvent = checked_cast(*event); auto key = static_cast( int(keyEvent.getKeyCode()) & int(Keys::K_MASK)); if (event->getType() == OPENMSX_KEY_DOWN_EVENT) { if (key == up ->getKey()) press = JOY_UP; else if (key == down ->getKey()) press = JOY_DOWN; else if (key == left ->getKey()) press = JOY_LEFT; else if (key == right->getKey()) press = JOY_RIGHT; else if (key == trigA->getKey()) press = JOY_BUTTONA; else if (key == trigB->getKey()) press = JOY_BUTTONB; } else { if (key == up ->getKey()) release = JOY_UP; else if (key == down ->getKey()) release = JOY_DOWN; else if (key == left ->getKey()) release = JOY_LEFT; else if (key == right->getKey()) release = JOY_RIGHT; else if (key == trigA->getKey()) release = JOY_BUTTONA; else if (key == trigB->getKey()) release = JOY_BUTTONB; } break; } default: // ignore break; } if (((status & ~press) | release) != status) { stateChangeDistributor.distributeNew(std::make_shared( time, name, press, release)); } } // StateChangeListener void KeyJoystick::signalStateChange(const shared_ptr& event) { auto kjs = dynamic_cast(event.get()); if (!kjs) return; if (kjs->getName() != name) return; status = (status & ~kjs->getPress()) | kjs->getRelease(); } void KeyJoystick::stopReplay(EmuTime::param time) { // TODO read actual host key state byte newStatus = JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT | JOY_BUTTONA | JOY_BUTTONB; if (newStatus != status) { byte release = newStatus & ~status; stateChangeDistributor.distributeNew(std::make_shared( time, name, 0, release)); } } // version 1: Initial version, the variable status was not serialized. // version 2: Also serialize the above variable, this is required for // record/replay, see comment in Keyboard.cc for more details. template void KeyJoystick::serialize(Archive& ar, unsigned version) { if (ar.versionAtLeast(version, 2)) { ar.serialize("status", status); } if (ar.isLoader() && isPluggedIn()) { plugHelper(*getConnector(), EmuTime::dummy()); } // no need to serialize 'pin8' } INSTANTIATE_SERIALIZE_METHODS(KeyJoystick); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, KeyJoystick, "KeyJoystick"); } // namespace openmsx openmsx-0.10.0/src/input/DummyJoystick.cc0000644000175000017500000000073412262345041021120 0ustar manuelmanuel00000000000000#include "DummyJoystick.hh" namespace openmsx { byte DummyJoystick::read(EmuTime::param /*time*/) { return 0x3F; } void DummyJoystick::write(byte /*value*/, EmuTime::param /*time*/) { // do nothing } string_ref DummyJoystick::getDescription() const { return ""; } void DummyJoystick::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { } void DummyJoystick::unplugHelper(EmuTime::param /*time*/) { } } // namespace openmsx openmsx-0.10.0/src/input/StateChange.hh0000644000175000017500000000151412262345041020502 0ustar manuelmanuel00000000000000#ifndef STATECHANGE_HH #define STATECHANGE_HH #include "EmuTime.hh" #include "serialize_meta.hh" #include "noncopyable.hh" namespace openmsx { /** Base class for all external MSX state changing events. * These are typically triggered by user input, like keyboard presses. The main * reason why these events exist is to be able to record and replay them. */ class StateChange : private noncopyable { public: virtual ~StateChange() {} EmuTime::param getTime() const { return time; } template void serialize(Archive& ar, unsigned /*version*/) { ar.serialize("time", time); } protected: StateChange() : time(EmuTime::zero) {} // for serialize StateChange(EmuTime::param time_) : time(time_) { } private: EmuTime time; }; REGISTER_BASE_CLASS(StateChange, "StateChange"); } // namespace openmsx #endif openmsx-0.10.0/src/input/JoyMega.hh0000644000175000017500000000405712262345041017654 0ustar manuelmanuel00000000000000#ifndef JOYMEGA_HH #define JOYMEGA_HH #include "JoystickDevice.hh" #include "MSXEventListener.hh" #include "StateChangeListener.hh" #include "serialize_meta.hh" #include namespace openmsx { class MSXEventDistributor; class StateChangeDistributor; class PluggingController; class JoyMega #ifndef SDL_JOYSTICK_DISABLED : public JoystickDevice, private MSXEventListener, private StateChangeListener #endif { public: static void registerAll(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, PluggingController& controller); JoyMega(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, SDL_Joystick* joystick); virtual ~JoyMega(); #ifndef SDL_JOYSTICK_DISABLED // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: void plugHelper2(); unsigned calcInitialState(); void checkTime(EmuTime::param time); void createEvent(EmuTime::param time, unsigned press, unsigned release); void createEvent(EmuTime::param time, unsigned newStatus); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); MSXEventDistributor& eventDistributor; StateChangeDistributor& stateChangeDistributor; SDL_Joystick* const joystick; const unsigned joyNum; const std::string name; const std::string desc; EmuTime lastTime; unsigned status; byte cycle; // 0-7 byte cycleMask; // 1 or 7 #endif // SDL_JOYSTICK_DISABLED }; } // namespace openmsx #endif openmsx-0.10.0/src/input/Trackball.cc0000644000175000017500000001554612262345041020213 0ustar manuelmanuel00000000000000#include "Trackball.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "InputEvents.hh" #include "StateChange.hh" #include "checked_cast.hh" #include "serialize.hh" #include "serialize_meta.hh" #include // * Implementation based on information we received from 'n_n'. // It might not be 100% accurate. But games like 'Hole in one' already work. // * Initially the 'trackball detection' code didn't work properly in openMSX // while it did work in meisei. Meisei had some special cases implemented for // the first read after reset. After some investigation I figured out some // code without special cases that also works as expected. Most software // seems to work now, though the detailed behaviour is still not tested // against the real hardware. using std::string; using std::shared_ptr; namespace openmsx { class TrackballState : public StateChange { public: TrackballState() {} // for serialize TrackballState(EmuTime::param time, int deltaX_, int deltaY_, byte press_, byte release_) : StateChange(time) , deltaX(deltaX_), deltaY(deltaY_) , press(press_), release(release_) {} int getDeltaX() const { return deltaX; } int getDeltaY() const { return deltaY; } byte getPress() const { return press; } byte getRelease() const { return release; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("deltaX", deltaX); ar.serialize("deltaY", deltaY); ar.serialize("press", press); ar.serialize("release", release); } private: int deltaX, deltaY; byte press, release; }; REGISTER_POLYMORPHIC_CLASS(StateChange, TrackballState, "TrackballState"); Trackball::Trackball(MSXEventDistributor& eventDistributor_, StateChangeDistributor& stateChangeDistributor_) : eventDistributor(eventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , deltaX(0), deltaY(0) , lastValue(0) , status(JOY_BUTTONA | JOY_BUTTONB) { } Trackball::~Trackball() { if (isPluggedIn()) { Trackball::unplugHelper(EmuTime::dummy()); } } // Pluggable const string& Trackball::getName() const { static const string name("trackball"); return name; } string_ref Trackball::getDescription() const { return "MSX Trackball"; } void Trackball::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { eventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); deltaX = deltaY = 0; } void Trackball::unplugHelper(EmuTime::param /*time*/) { stateChangeDistributor.unregisterListener(*this); eventDistributor.unregisterEventListener(*this); } // JoystickDevice byte Trackball::read(EmuTime::param /*time*/) { // From the Sony GB-7 Service manual: // http://cdn.preterhuman.net/texts/computing/msx/sonygb7sm.pdf // * The counter seems to be 8-bit wide, though only 4 bits (bit 7 and // 2-0) are connected to the MSX. Looking at the M60226 block diagram // in more detail shows that the (up/down-)counters have a 'HP' // input, in the GB7 this input is hardwired to GND. My *guess* is // that HP stands for either 'Half-' or 'High-precision' and that it // selects between either 4 or 8 bits saturation. // The bug report '#477 Trackball emulation overflow values too // easily' contains a small movie. Some very rough calculations // indicate that when you move a cursor 50 times per second, 8 pixels // per step, you get about the same speed as in that move. // So even though both 4 and 8 bit clipping *seem* to be possible, // this code only implements 4 bit clipping. // * It also contains a test program to read the trackball position. // This program first reads the (X or Y) value and only then toggles // pin 8. This seems to suggest the actual (X or Y) value is always // present on reads and toggling pin 8 resets this value and switches // to the other axis. auto delta = (lastValue & 4) ? deltaY : deltaX; return (status & ~0x0F) | ((delta + 8) & 0x0F); } void Trackball::write(byte value, EmuTime::param /*time*/) { byte diff = lastValue ^ value; lastValue = value; if (diff & 0x4) { // pin 8 flipped if (value & 4) { deltaX = 0; } else { deltaY = 0; } } } // MSXEventListener void Trackball::signalEvent(const shared_ptr& event, EmuTime::param time) { switch (event->getType()) { case OPENMSX_MOUSE_MOTION_EVENT: { auto& mev = checked_cast(*event); static const int SCALE = 2; int dx = mev.getX() / SCALE; int dy = mev.getY() / SCALE; if ((dx != 0) || (dy != 0)) { createTrackballStateChange(time, dx, dy, 0, 0); } break; } case OPENMSX_MOUSE_BUTTON_DOWN_EVENT: { auto& butEv = checked_cast(*event); switch (butEv.getButton()) { case MouseButtonEvent::LEFT: createTrackballStateChange(time, 0, 0, JOY_BUTTONA, 0); break; case MouseButtonEvent::RIGHT: createTrackballStateChange(time, 0, 0, JOY_BUTTONB, 0); break; default: // ignore other buttons break; } break; } case OPENMSX_MOUSE_BUTTON_UP_EVENT: { auto& butEv = checked_cast(*event); switch (butEv.getButton()) { case MouseButtonEvent::LEFT: createTrackballStateChange(time, 0, 0, 0, JOY_BUTTONA); break; case MouseButtonEvent::RIGHT: createTrackballStateChange(time, 0, 0, 0, JOY_BUTTONB); break; default: // ignore other buttons break; } break; } default: // ignore break; } } void Trackball::createTrackballStateChange( EmuTime::param time, int deltaX, int deltaY, byte press, byte release) { stateChangeDistributor.distributeNew(std::make_shared( time, deltaX, deltaY, press, release)); } // StateChangeListener void Trackball::signalStateChange(const shared_ptr& event) { auto ts = dynamic_cast(event.get()); if (!ts) return; deltaX = std::min(7, std::max(-8, deltaX + ts->getDeltaX())); deltaY = std::min(7, std::max(-8, deltaY + ts->getDeltaY())); status = (status & ~ts->getPress()) | ts->getRelease(); } void Trackball::stopReplay(EmuTime::param time) { // TODO Get actual mouse button(s) state. Is it worth the trouble? byte release = (JOY_BUTTONA | JOY_BUTTONB) & ~status; if ((deltaX != 0) || (deltaY != 0) || (release != 0)) { stateChangeDistributor.distributeNew( std::make_shared( time, -deltaX, -deltaY, 0, release)); } } template void Trackball::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("deltaX", deltaX); ar.serialize("deltaY", deltaY); ar.serialize("lastValue", lastValue); ar.serialize("status", status); if (ar.isLoader() && isPluggedIn()) { plugHelper(*getConnector(), EmuTime::dummy()); } } INSTANTIATE_SERIALIZE_METHODS(Trackball); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, Trackball, "Trackball"); } // namespace openmsx openmsx-0.10.0/src/input/ArkanoidPad.cc0000644000175000017500000001313312262345041020457 0ustar manuelmanuel00000000000000#include "ArkanoidPad.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "InputEvents.hh" #include "StateChange.hh" #include "checked_cast.hh" #include "serialize.hh" #include "serialize_meta.hh" #include // Implemented mostly according to the info here: http://www.msx.org/forumtopic7661.html // This is absolutely not accurate, but good enough to make the pad work in the // Arkanoid games. // The hardware works with a 556 dual timer that's unemulated here, it requires // short delays at each shift, and a long delay at latching. Too short delays // will cause garbage reads, and too long delays at shifting will eventually // cause the shift register bits to return to 0. using std::string; using std::shared_ptr; using std::make_shared; namespace openmsx { static const int POS_MIN = 55; // measured by hap static const int POS_MAX = 325; // measured by hap static const int POS_CENTER = 236; // approx. middle used by games static const int SCALE = 2; class ArkanoidState : public StateChange { public: ArkanoidState() {} // for serialize ArkanoidState(EmuTime::param time, int delta_, bool press_, bool release_) : StateChange(time) , delta(delta_), press(press_), release(release_) {} int getDelta() const { return delta; } bool getPress() const { return press; } bool getRelease() const { return release; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("delta", delta); ar.serialize("press", press); ar.serialize("release", release); } private: int delta; bool press, release; }; REGISTER_POLYMORPHIC_CLASS(StateChange, ArkanoidState, "ArkanoidState"); ArkanoidPad::ArkanoidPad(MSXEventDistributor& eventDistributor_, StateChangeDistributor& stateChangeDistributor_) : eventDistributor(eventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , shiftreg(0) // the 9 bit shift degrades to 0 , dialpos(POS_CENTER) , buttonStatus(0x3E) , lastValue(0) { } ArkanoidPad::~ArkanoidPad() { if (isPluggedIn()) { ArkanoidPad::unplugHelper(EmuTime::dummy()); } } // Pluggable const string& ArkanoidPad::getName() const { static const string name("arkanoidpad"); return name; } string_ref ArkanoidPad::getDescription() const { return "Arkanoid pad"; } void ArkanoidPad::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { eventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); } void ArkanoidPad::unplugHelper(EmuTime::param /*time*/) { stateChangeDistributor.unregisterListener(*this); eventDistributor.unregisterEventListener(*this); } // JoystickDevice byte ArkanoidPad::read(EmuTime::param /*time*/) { return buttonStatus | ((shiftreg & 0x100) >> 8); } void ArkanoidPad::write(byte value, EmuTime::param /*time*/) { byte diff = lastValue ^ value; lastValue = value; if (diff & value & 0x4) { // pin 8 from low to high: copy dial position into shift reg shiftreg = dialpos; } if (diff & value & 0x1) { // pin 6 from low to high: shift the shift reg shiftreg = (shiftreg << 1) | (shiftreg & 1); // bit 0's value is 'shifted in' } } // MSXEventListener void ArkanoidPad::signalEvent(const shared_ptr& event, EmuTime::param time) { switch (event->getType()) { case OPENMSX_MOUSE_MOTION_EVENT: { auto& mEvent = checked_cast(*event); int newPos = std::min(POS_MAX, std::max(POS_MIN, dialpos + mEvent.getX() / SCALE)); int delta = newPos - dialpos; if (delta != 0) { stateChangeDistributor.distributeNew( make_shared( time, delta, false, false)); } break; } case OPENMSX_MOUSE_BUTTON_DOWN_EVENT: // any button will press the Arkanoid Pad button if (buttonStatus & 2) { stateChangeDistributor.distributeNew( make_shared( time, 0, true, false)); } break; case OPENMSX_MOUSE_BUTTON_UP_EVENT: // any button will unpress the Arkanoid Pad button if (!(buttonStatus & 2)) { stateChangeDistributor.distributeNew( make_shared( time, 0, false, true)); } break; default: // ignore break; } } // StateChangeListener void ArkanoidPad::signalStateChange(const shared_ptr& event) { auto as = dynamic_cast(event.get()); if (!as) return; dialpos += as->getDelta(); if (as->getPress()) buttonStatus &= ~2; if (as->getRelease()) buttonStatus |= 2; } void ArkanoidPad::stopReplay(EmuTime::param time) { // TODO Get actual mouse button(s) state. Is it worth the trouble? int delta = POS_CENTER - dialpos; bool release = (buttonStatus & 2) == 0; if ((delta != 0) || release) { stateChangeDistributor.distributeNew(make_shared( time, delta, false, release)); } } // version 1: Initial version, the variables dialpos and buttonStatus were not // serialized. // version 2: Also serialize the above variables, this is required for // record/replay, see comment in Keyboard.cc for more details. template void ArkanoidPad::serialize(Archive& ar, unsigned version) { ar.serialize("shiftreg", shiftreg); ar.serialize("lastValue", lastValue); if (ar.versionAtLeast(version, 2)) { ar.serialize("dialpos", dialpos); ar.serialize("buttonStatus", buttonStatus); } if (ar.isLoader() && isPluggedIn()) { plugHelper(*getConnector(), EmuTime::dummy()); } } INSTANTIATE_SERIALIZE_METHODS(ArkanoidPad); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, ArkanoidPad, "ArkanoidPad"); } // namespace openmsx openmsx-0.10.0/src/input/JoystickPort.hh0000644000175000017500000000236212262345041020762 0ustar manuelmanuel00000000000000#ifndef JOYSTICKPORT_HH #define JOYSTICKPORT_HH #include "Connector.hh" #include "openmsx.hh" namespace openmsx { class JoystickDevice; class PluggingController; class JoystickPortIf { public: virtual ~JoystickPortIf() {} virtual byte read(EmuTime::param time) = 0; virtual void write(byte value, EmuTime::param time) = 0; protected: JoystickPortIf() {} }; class JoystickPort : public JoystickPortIf, public Connector { public: JoystickPort(PluggingController& pluggingController, string_ref name, const std::string& description); virtual ~JoystickPort(); JoystickDevice& getPluggedJoyDev() const; // Connector virtual const std::string getDescription() const; virtual string_ref getClass() const; virtual void plug(Pluggable& device, EmuTime::param time); virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: void writeDirect(byte value, EmuTime::param time); byte lastValue; const std::string description; }; class DummyJoystickPort : public JoystickPortIf { public: virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); }; } // namespace openmsx #endif openmsx-0.10.0/src/input/Touchpad.hh0000644000175000017500000000405612262345041020067 0ustar manuelmanuel00000000000000#ifndef TOUCHPAD_HH #define TOUCHPAD_HH #include "JoystickDevice.hh" #include "MSXEventListener.hh" #include "StateChangeListener.hh" #include namespace openmsx { class MSXEventDistributor; class StateChangeDistributor; class CommandController; class StringSetting; class TclObject; class Touchpad : public JoystickDevice, private MSXEventListener , private StateChangeListener { public: Touchpad(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor, CommandController& commandController); virtual ~Touchpad(); template void serialize(Archive& ar, unsigned version); private: void createTouchpadStateChange(EmuTime::param time, byte x, byte y, bool touch, bool button); void parseTransformMatrix(const TclObject& value); void transformCoords(int& x, int& y); // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); MSXEventDistributor& eventDistributor; StateChangeDistributor& stateChangeDistributor; std::unique_ptr transformSetting; double m[2][3]; // transformation matrix EmuTime start; // last time when CS switched 0->1 int hostX, hostY; // host state byte hostButtons; // byte x, y; // msx state (different from host state bool touch, button; // during replay) byte shift; // shift register to both transmit and receive data byte channel; // [0..3] 0->x, 3->y, 1,2->not used byte last; // last written data, to detect transitions }; } // namespace openmsx #endif openmsx-0.10.0/src/input/MagicKey.hh0000644000175000017500000000111512262345041020002 0ustar manuelmanuel00000000000000#ifndef MAGICKEY_HH #define MAGICKEY_HH #include "JoystickDevice.hh" namespace openmsx { class MagicKey : public JoystickDevice { public: // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); }; } // namespace openmsx #endif openmsx-0.10.0/src/input/ArkanoidPad.hh0000644000175000017500000000270212262345041020471 0ustar manuelmanuel00000000000000#ifndef ARKANOIDPAD_HH #define ARKANOIDPAD_HH #include "JoystickDevice.hh" #include "MSXEventListener.hh" #include "StateChangeListener.hh" #include "serialize_meta.hh" namespace openmsx { class MSXEventDistributor; class StateChangeDistributor; class ArkanoidPad : public JoystickDevice, private MSXEventListener , private StateChangeListener { public: explicit ArkanoidPad(MSXEventDistributor& eventDistributor, StateChangeDistributor& stateChangeDistributor); virtual ~ArkanoidPad(); template void serialize(Archive& ar, unsigned version); private: // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); MSXEventDistributor& eventDistributor; StateChangeDistributor& stateChangeDistributor; int shiftreg; int dialpos; byte buttonStatus; byte lastValue; }; SERIALIZE_CLASS_VERSION(ArkanoidPad, 2); } // namespace openmsx #endif openmsx-0.10.0/src/input/NinjaTap.hh0000644000175000017500000000115412262345041020020 0ustar manuelmanuel00000000000000#ifndef NINJATAP_HH #define NINJATAP_HH #include "JoyTap.hh" namespace openmsx { class NinjaTap : public JoyTap { public: NinjaTap(PluggingController& pluggingController, const std::string& name); // Pluggable virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); // JoystickDevice virtual byte read(EmuTime::param time); virtual void write(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte status; byte previous; byte buf[4]; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/JoyTap.hh0000644000175000017500000000262612262345041017527 0ustar manuelmanuel00000000000000#ifndef JOYTAP_HH #define JOYTAP_HH #include "JoystickDevice.hh" #include "serialize_meta.hh" #include namespace openmsx { class PluggingController; class JoystickPort; /** This device is pluged in into the joyports and consolidates several other * joysticks plugged into it. This joytap simply ANDs all the joystick * outputs, acting as a simple wiring of all digital joysticks into one * connector. * This is the base class for the NinjaTap device and the FNano2 multiplayer * extension, who basicly have other read and write methods */ class JoyTap : public JoystickDevice { public: JoyTap(PluggingController& pluggingController, const std::string& name); virtual ~JoyTap(); // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // JoystickDevice byte read(EmuTime::param time); void write(byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); protected: void createPorts(PluggingController& pluggingController, const std::string& baseDescription); std::unique_ptr slaves[4]; PluggingController& pluggingController; private: const std::string name; }; REGISTER_BASE_NAME_HELPER(JoyTap, "JoyTap"); } // namespace openmsx #endif openmsx-0.10.0/src/input/SETetrisDongle.cc0000644000175000017500000000236212262345041021137 0ustar manuelmanuel00000000000000#include "SETetrisDongle.hh" #include "serialize.hh" #include "serialize_meta.hh" namespace openmsx { SETetrisDongle::SETetrisDongle() { status = JOY_UP | JOY_DOWN | JOY_LEFT | JOY_RIGHT | JOY_BUTTONA | JOY_BUTTONB; } // Pluggable const std::string& SETetrisDongle::getName() const { static const std::string name = "tetris2-protection"; return name; } string_ref SETetrisDongle::getDescription() const { return "Tetris II Special Edition dongle"; } void SETetrisDongle::plugHelper( Connector& /*connector*/, EmuTime::param /*time*/) { } void SETetrisDongle::unplugHelper(EmuTime::param /*time*/) { } // JoystickDevice byte SETetrisDongle::read(EmuTime::param /*time*/) { return status; } void SETetrisDongle::write(byte value, EmuTime::param /*time*/) { // Original device used 4 NOR ports // pin4 will be value of pin7 if (value & 2) { status |= JOY_RIGHT; } else { status &= ~JOY_RIGHT; } } template void SETetrisDongle::serialize(Archive& /*ar*/, unsigned /*version*/) { // no need to serialize 'status', port will anyway be re-written // on de-serialize } INSTANTIATE_SERIALIZE_METHODS(SETetrisDongle); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, SETetrisDongle, "SETetrisDongle"); } // namespace openmsx openmsx-0.10.0/src/input/MagicKey.cc0000644000175000017500000000160312262345041017772 0ustar manuelmanuel00000000000000#include "MagicKey.hh" #include "serialize.hh" #include "serialize_meta.hh" namespace openmsx { // Pluggable const std::string& MagicKey::getName() const { static const std::string NAME = "magic-key"; return NAME; } string_ref MagicKey::getDescription() const { return "Dongle used by some Japanese games to enable cheat mode"; } void MagicKey::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { } void MagicKey::unplugHelper(EmuTime::param /*time*/) { } // JoystickDevice byte MagicKey::read(EmuTime::param /*time*/) { return JOY_BUTTONB | JOY_BUTTONA | JOY_RIGHT | JOY_LEFT; } void MagicKey::write(byte /*value*/, EmuTime::param /*time*/) { } template void MagicKey::serialize(Archive& /*ar*/, unsigned /*version*/) { } INSTANTIATE_SERIALIZE_METHODS(MagicKey); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, MagicKey, "MagicKey"); } // namespace openmsx openmsx-0.10.0/src/input/RecordedCommand.cc0000644000175000017500000001063612262345041021335 0ustar manuelmanuel00000000000000#include "RecordedCommand.hh" #include "CommandController.hh" #include "StateChangeDistributor.hh" #include "TclObject.hh" #include "Scheduler.hh" #include "StateChange.hh" #include "ScopedAssign.hh" #include "serialize.hh" #include "serialize_stl.hh" #include "checked_cast.hh" #include "unreachable.hh" #include "memory.hh" using std::vector; using std::string; namespace openmsx { RecordedCommand::RecordedCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor_, Scheduler& scheduler_, string_ref name) : Command(commandController, name) , stateChangeDistributor(stateChangeDistributor_) , scheduler(scheduler_) , dummyResultObject(make_unique(getInterpreter())) , currentResultObject(dummyResultObject.get()) { stateChangeDistributor.registerListener(*this); } RecordedCommand::~RecordedCommand() { stateChangeDistributor.unregisterListener(*this); } void RecordedCommand::execute(const vector& tokens, TclObject& result) { auto time = scheduler.getCurrentTime(); if (needRecord(tokens)) { ScopedAssign sa(currentResultObject, &result); stateChangeDistributor.distributeNew( std::make_shared(tokens, time)); } else { execute(tokens, result, time); } } bool RecordedCommand::needRecord(const vector& tokens) const { vector strings; strings.reserve(tokens.size()); for (auto& t : tokens) { strings.push_back(t.getString().str()); } return needRecord(strings); } bool RecordedCommand::needRecord(const vector& /*tokens*/) const { return true; } static string_ref getBaseName(string_ref str) { auto pos = str.rfind("::"); return (pos == string_ref::npos) ? str : str.substr(pos + 2); } void RecordedCommand::signalStateChange(const std::shared_ptr& event) { auto* commandEvent = dynamic_cast(event.get()); if (!commandEvent) return; auto& tokens = commandEvent->getTokens(); if (getBaseName(tokens[0].getString()) != getName()) return; if (needRecord(tokens)) { execute(tokens, *currentResultObject, commandEvent->getTime()); } else { // Normally this shouldn't happen. But it's possible in case // we're replaying a replay file that has manual edits in the // event log. It's crucial for security that we don't blindly // execute such commands. We already only execute // RecordedCommands, but we also need a strict check that // only commands that would be recorded are also replayed. // For example: // debug set_bp 0x0038 true {} // The debug write/write_block commands should be recorded and // replayed, but via the set_bp it would be possible to // execute arbitrary Tcl code. } } void RecordedCommand::stopReplay(EmuTime::param /*time*/) { // nothing } void RecordedCommand::execute(const vector& tokens, TclObject& result, EmuTime::param time) { vector strings; strings.reserve(tokens.size()); for (auto& t : tokens) { strings.push_back(t.getString().str()); } result.setString(execute(strings, time)); } string RecordedCommand::execute(const vector& /*tokens*/, EmuTime::param /*time*/) { // either this method or the method above should be reimplemented // by the subclasses UNREACHABLE; return ""; } // class MSXCommandEvent MSXCommandEvent::MSXCommandEvent(const vector& tokens_, EmuTime::param time) : StateChange(time) { for (auto& t : tokens_) { tokens.push_back(TclObject(t)); } } MSXCommandEvent::MSXCommandEvent(const vector& tokens_, EmuTime::param time) : StateChange(time) , tokens(tokens_) { } MSXCommandEvent::~MSXCommandEvent() { } const vector& MSXCommandEvent::getTokens() const { return tokens; } template void MSXCommandEvent::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); // serialize vector as vector vector str; if (!ar.isLoader()) { for (auto& t : tokens) { str.push_back(t.getString().str()); } } ar.serialize("tokens", str); if (ar.isLoader()) { assert(tokens.empty()); for (auto& s : str) { tokens.push_back(TclObject(s)); } } } REGISTER_POLYMORPHIC_CLASS(StateChange, MSXCommandEvent, "MSXCommandEvent"); } // namespace openmsx openmsx-0.10.0/src/input/JoystickPort.cc0000644000175000017500000000373612262345041020756 0ustar manuelmanuel00000000000000#include "JoystickPort.hh" #include "JoystickDevice.hh" #include "DummyJoystick.hh" #include "PluggingController.hh" #include "checked_cast.hh" #include "serialize.hh" #include "memory.hh" #include using std::string; namespace openmsx { JoystickPort::JoystickPort(PluggingController& pluggingController_, string_ref name, const string& description_) : Connector(pluggingController_, name, make_unique()) , lastValue(255) // != 0 , description(description_) { } JoystickPort::~JoystickPort() { } const string JoystickPort::getDescription() const { return description; } string_ref JoystickPort::getClass() const { return "Joystick Port"; } JoystickDevice& JoystickPort::getPluggedJoyDev() const { return *checked_cast(&getPlugged()); } void JoystickPort::plug(Pluggable& device, EmuTime::param time) { Connector::plug(device, time); getPluggedJoyDev().write(lastValue, time); } byte JoystickPort::read(EmuTime::param time) { return getPluggedJoyDev().read(time); } void JoystickPort::write(byte value, EmuTime::param time) { if (lastValue != value) writeDirect(value, time); } void JoystickPort::writeDirect(byte value, EmuTime::param time) { lastValue = value; getPluggedJoyDev().write(value, time); } template void JoystickPort::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); if (ar.isLoader()) { // The value of 'lastValue', is already restored via MSXPSG, // but we still need to re-write this value to the plugged // devices (do this after those devices have been re-plugged). writeDirect(lastValue, getPluggingController().getCurrentTime()); } } INSTANTIATE_SERIALIZE_METHODS(JoystickPort); // class DummyJoystickPort byte DummyJoystickPort::read(EmuTime::param /*time*/) { return 0x3F; // do as-if nothing is connected } void DummyJoystickPort::write(byte /*value*/, EmuTime::param /*time*/) { // ignore writes } } // namespace openmsx openmsx-0.10.0/src/input/Keyboard.hh0000644000175000017500000001244512262345041020061 0ustar manuelmanuel00000000000000#ifndef KEYBOARD_HH #define KEYBOARD_HH #include "MSXEventListener.hh" #include "StateChangeListener.hh" #include "Schedulable.hh" #include "serialize_meta.hh" #include "string_ref.hh" #include "openmsx.hh" #include #include namespace openmsx { class MSXMotherBoard; class Scheduler; class CommandController; class EventDistributor; class MSXEventDistributor; class StateChangeDistributor; class KeyMatrixUpCmd; class KeyMatrixDownCmd; class KeyInserter; class KeyEvent; class CapsLockAligner; class KeyboardSettings; class MsxKeyEventQueue; class UnicodeKeymap; class KeybDebuggable; class StateChange; class Keyboard : private MSXEventListener, private StateChangeListener, private Schedulable { public: static const unsigned NR_KEYROWS = 16; /** Constructs a new Keyboard object. * @param motherBoard ref to the motherBoard * @param scheduler ref to the scheduler * @param commandController ref to the command controller * @param eventDistributor ref to the emu event distributor * @param msxEventDistributor ref to the user input event distributor * @param stateChangeDistributor ref to the state change distributor * @param keyboardType contains filename extension of unicode keymap file * @param hasKeypad turn MSX keypad on/off * @param hasYesNoKeys this keyboard has (Japanese) Yes/No keys * @param keyGhosting turn keyGhosting on/off * @param keyGhostingSGCprotected Shift, Graph and Code are keyGhosting protected * @param codeKanaLocks CodeKana key behave as a lock key on this machine * @param graphLocks Graph key behave as a lock key on this machine */ Keyboard(MSXMotherBoard& motherBoard, Scheduler& scheduler, CommandController& commandController, EventDistributor& eventDistributor, MSXEventDistributor& msxEventDistributor, StateChangeDistributor& stateChangeDistributor, string_ref keyboardType, bool hasKeypad, bool hasYesNoKeys, bool keyGhosting, bool keyGhostingSGCprotected, bool codeKanaLocks, bool graphLocks); virtual ~Keyboard(); /** Returns a pointer to the current KeyBoard matrix */ const byte* getKeys(); void transferHostKeyMatrix(const Keyboard& source); template void serialize(Archive& ar, unsigned version); private: // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); // StateChangeListener virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); // Schedulable virtual void executeUntil(EmuTime::param time, int userData); void pressKeyMatrixEvent (EmuTime::param time, byte row, byte press); void releaseKeyMatrixEvent(EmuTime::param time, byte row, byte release); void changeKeyMatrixEvent (EmuTime::param time, byte row, byte newValue); void processDeadKeyEvent(unsigned n, EmuTime::param time, bool down); void processCapslockEvent(EmuTime::param time, bool down); void processCodeKanaChange(EmuTime::param time, bool down); void processGraphChange(EmuTime::param time, bool down); void processKeypadEnterKey(EmuTime::param time, bool down); void processSdlKey(EmuTime::param time, bool down, int key); bool processQueuedEvent(const Event& event, EmuTime::param time); bool processKeyEvent(EmuTime::param time, bool down, const KeyEvent& keyEvent); void updateKeyMatrix(EmuTime::param time, bool down, int row, byte mask); void doKeyGhosting(); std::string processCmd(const std::vector& tokens, bool up); bool pressUnicodeByUser(EmuTime::param time, unsigned unicode, bool down); int pressAscii(unsigned unicode, bool down); void pressLockKeys(int lockKeysMask, bool down); bool commonKeys(unsigned unicode1, unsigned unicode2); void debug(const char* format, ...); CommandController& commandController; MSXEventDistributor& msxEventDistributor; StateChangeDistributor& stateChangeDistributor; friend class KeyMatrixUpCmd; friend class KeyMatrixDownCmd; friend class KeyInserter; friend class CapsLockAligner; friend class MsxKeyEventQueue; static const int MAX_KEYSYM = 0x150; static const byte keyTab[MAX_KEYSYM]; const std::unique_ptr keyMatrixUpCmd; const std::unique_ptr keyMatrixDownCmd; const std::unique_ptr keyTypeCmd; const std::unique_ptr capsLockAligner; const std::unique_ptr keyboardSettings; const std::unique_ptr msxKeyEventQueue; const std::unique_ptr keybDebuggable; const std::unique_ptr unicodeKeymap; unsigned dynKeymap[MAX_KEYSYM]; byte cmdKeyMatrix [NR_KEYROWS]; // for keymatrix/type command byte userKeyMatrix[NR_KEYROWS]; // pressed user keys (live or replay) byte hostKeyMatrix[NR_KEYROWS]; // always in sync with host keyb, also during replay byte keyMatrix [NR_KEYROWS]; // combination of cmdKeyMatrix and userKeyMatrix byte msxmodifiers; const bool hasKeypad; const bool hasYesNoKeys; const bool keyGhosting; const bool keyGhostingSGCprotected; const bool codeKanaLocks; const bool graphLocks; const bool sdlReleasesCapslock; bool keysChanged; bool msxCapsLockOn; bool msxCodeKanaLockOn; bool msxGraphLockOn; }; SERIALIZE_CLASS_VERSION(Keyboard, 2); } // namespace openmsx #endif openmsx-0.10.0/src/input/MSXEventDistributor.cc0000644000175000017500000000247212262345041022212 0ustar manuelmanuel00000000000000#include "MSXEventDistributor.hh" #include "MSXEventListener.hh" #include #include namespace openmsx { MSXEventDistributor::MSXEventDistributor() { } MSXEventDistributor::~MSXEventDistributor() { assert(listeners.empty()); } bool MSXEventDistributor::isRegistered(MSXEventListener* listener) const { return find(listeners.begin(), listeners.end(), listener) != listeners.end(); } void MSXEventDistributor::registerEventListener(MSXEventListener& listener) { assert(!isRegistered(&listener)); listeners.push_back(&listener); } void MSXEventDistributor::unregisterEventListener(MSXEventListener& listener) { assert(isRegistered(&listener)); listeners.erase(find(listeners.begin(), listeners.end(), &listener)); } void MSXEventDistributor::distributeEvent(const EventPtr& event, EmuTime::param time) { // Iterate over a copy because signalEvent() may indirect call back into // registerEventListener(). // e.g. signalEvent() -> .. -> PlugCmd::execute() -> .. -> // Connector::plug() -> .. -> Joystick::plugHelper() -> // registerEventListener() auto copy = listeners; for (auto& l : copy) { if (isRegistered(l)) { // it's possible the listener unregistered itself // (but is still present in the copy) l->signalEvent(event, time); } } } } // namespace openmsx openmsx-0.10.0/src/input/StateChangeDistributor.hh0000644000175000017500000000526412262345041022743 0ustar manuelmanuel00000000000000#ifndef STATECHANGEDISTRIBUTOR_HH #define STATECHANGEDISTRIBUTOR_HH #include "EmuTime.hh" #include "noncopyable.hh" #include #include namespace openmsx { class StateChangeListener; class StateChangeRecorder; class StateChange; class StateChangeDistributor : private noncopyable { public: typedef std::shared_ptr EventPtr; StateChangeDistributor(); ~StateChangeDistributor(); /** (Un)registers the given object to receive state change events. * @param listener Listener that will be notified when an event arrives. */ void registerListener (StateChangeListener& listener); void unregisterListener(StateChangeListener& listener); /** (Un)registers the given object to receive state change events. * @param recorder Listener that will be notified when an event arrives. * These two methods are very similar to the two above. The difference * is that there can be at most one registered recorder. This recorder * object is always the first object that gets informed about state * changing events. */ void registerRecorder (StateChangeRecorder& recorder); void unregisterRecorder(StateChangeRecorder& recorder); /** Deliver the event to all registered listeners * MSX input devices should call the distributeNew() version, only the * replayer should call the distributeReplay() version. * These two different versions are used to detect the transition from * replayed events to live events. Note that a transition from live to * replay is not allowed. This transition should be done by creating a * new StateChangeDistributor object (object always starts in replay * state), but this is automatically taken care of because replay * always starts from a freshly restored snapshot. * @param event The event */ void distributeNew (const EventPtr& event); void distributeReplay(const EventPtr& event); /** Explicitly stop replay. * Should be called when replay->live transition cannot be signaled via * a new event, so for example when we reach the end of the replay log. * It's OK to call this method when replay was already stopped, in that * case this call has no effect. */ void stopReplay(EmuTime::param time); /** * Set viewOnlyMode. Call this if you don't want distributeNew events * to stop replaying and go to live events (value=true). * @param value false if new events stop replay mode */ void setViewOnlyMode(bool value); bool isViewOnlyMode() const; bool isReplaying() const; private: bool isRegistered(StateChangeListener* listener) const; void distribute(const EventPtr& event); std::vector listeners; StateChangeRecorder* recorder; bool viewOnlyMode; }; } // namespace openmsx #endif openmsx-0.10.0/src/input/MSXEventListener.hh0000644000175000017500000000072512262345041021476 0ustar manuelmanuel00000000000000#ifndef MSXEVENTLISTENER_HH #define MSXEVENTLISTENER_HH #include "EmuTime.hh" #include namespace openmsx { class Event; class MSXEventListener { public: virtual ~MSXEventListener() {} /** This method gets called when an event you are subscribed to occurs. */ virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time) = 0; protected: MSXEventListener() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/input/KeyboardSettings.cc0000644000175000017500000001050112262345041021557 0ustar manuelmanuel00000000000000#include "KeyboardSettings.hh" #include "EnumSetting.hh" #include "BooleanSetting.hh" #include "memory.hh" #include namespace openmsx { KeyboardSettings::KeyboardSettings(CommandController& commandController) : alwaysEnableKeypad(make_unique( commandController, "kbd_numkeypad_always_enabled", "Numeric keypad is always enabled, even on an MSX that does not have one", false)) , traceKeyPresses(make_unique( commandController, "kbd_trace_key_presses", "Trace key presses (show SDL key code, SDL modifiers and Unicode code-point value)", false, Setting::DONT_SAVE)) , autoToggleCodeKanaLock(make_unique(commandController, "kbd_auto_toggle_code_kana_lock", "Automatically toggle the CODE/KANA lock, based on the characters entered on the host keyboard", true)) { EnumSetting::Map allowedKeys; allowedKeys.push_back(std::make_pair("RALT", Keys::K_RALT)); allowedKeys.push_back(std::make_pair("MENU", Keys::K_MENU)); allowedKeys.push_back(std::make_pair("RCTRL", Keys::K_RCTRL)); allowedKeys.push_back(std::make_pair("HENKAN_MODE", Keys::K_HENKAN_MODE)); allowedKeys.push_back(std::make_pair("RSHIFT", Keys::K_RSHIFT)); allowedKeys.push_back(std::make_pair("RMETA", Keys::K_RMETA)); allowedKeys.push_back(std::make_pair("LMETA", Keys::K_LMETA)); allowedKeys.push_back(std::make_pair("LSUPER", Keys::K_LSUPER)); allowedKeys.push_back(std::make_pair("RSUPER", Keys::K_RSUPER)); allowedKeys.push_back(std::make_pair("HELP", Keys::K_HELP)); allowedKeys.push_back(std::make_pair("UNDO", Keys::K_UNDO)); allowedKeys.push_back(std::make_pair("END", Keys::K_END)); allowedKeys.push_back(std::make_pair("PAGEUP", Keys::K_PAGEUP)); allowedKeys.push_back(std::make_pair("PAGEDOWN", Keys::K_PAGEDOWN)); codeKanaHostKey = make_unique>( commandController, "kbd_code_kana_host_key", "Host key that maps to the MSX CODE/KANA key. Please note that the HENKAN_MODE key only exists on Japanese host keyboards)", Keys::K_RALT, allowedKeys); deadkeyHostKey[0] = make_unique>( commandController, "kbd_deadkey1_host_key", "Host key that maps to deadkey 1. Not applicable to Japanese and Korean MSX models", Keys::K_RCTRL, allowedKeys); deadkeyHostKey[1] = make_unique>( commandController, "kbd_deadkey2_host_key", "Host key that maps to deadkey 2. Only applicable to Brazilian MSX models (Sharp Hotbit and Gradiente)", Keys::K_PAGEUP, allowedKeys); deadkeyHostKey[2] = make_unique>( commandController, "kbd_deadkey3_host_key", "Host key that maps to deadkey 3. Only applicable to Brazilian Sharp Hotbit MSX models", Keys::K_PAGEDOWN, allowedKeys); EnumSetting::Map kpEnterModeMap; kpEnterModeMap.push_back(std::make_pair("KEYPAD_COMMA", MSX_KP_COMMA)); kpEnterModeMap.push_back(std::make_pair("ENTER", MSX_ENTER)); kpEnterMode = make_unique>( commandController, "kbd_numkeypad_enter_key", "MSX key that the enter key on the host numeric keypad must map to", MSX_KP_COMMA, kpEnterModeMap); EnumSetting::Map mappingModeMap; mappingModeMap.push_back(std::make_pair("KEY", KEY_MAPPING)); mappingModeMap.push_back(std::make_pair("CHARACTER", CHARACTER_MAPPING)); mappingMode = make_unique>( commandController, "kbd_mapping_mode", "Keyboard mapping mode", CHARACTER_MAPPING, mappingModeMap); } KeyboardSettings::~KeyboardSettings() { } EnumSetting& KeyboardSettings::getCodeKanaHostKey() const { return *codeKanaHostKey; } Keys::KeyCode KeyboardSettings::getDeadkeyHostKey(unsigned n) const { assert(n < 3); return deadkeyHostKey[n]->getEnum(); } EnumSetting& KeyboardSettings::getKpEnterMode() const { return *kpEnterMode; } EnumSetting& KeyboardSettings::getMappingMode() const { return *mappingMode; } BooleanSetting& KeyboardSettings::getAlwaysEnableKeypad() const { return *alwaysEnableKeypad; } BooleanSetting& KeyboardSettings::getTraceKeyPresses() const { return *traceKeyPresses; } BooleanSetting& KeyboardSettings::getAutoToggleCodeKanaLock() const { return *autoToggleCodeKanaLock; } } // namespace openmsx openmsx-0.10.0/src/input/Keyboard.cc0000644000175000017500000014300412262345041020043 0ustar manuelmanuel00000000000000#include "Keyboard.hh" #include "KeyboardSettings.hh" #include "Keys.hh" #include "EventListener.hh" #include "EventDistributor.hh" #include "InputEventFactory.hh" #include "MSXEventDistributor.hh" #include "StateChangeDistributor.hh" #include "MSXMotherBoard.hh" #include "ReverseManager.hh" #include "MSXException.hh" #include "RecordedCommand.hh" #include "CommandException.hh" #include "SimpleDebuggable.hh" #include "InputEvents.hh" #include "StateChange.hh" #include "BooleanSetting.hh" #include "EnumSetting.hh" #include "UnicodeKeymap.hh" #include "utf8_checked.hh" #include "checked_cast.hh" #include "unreachable.hh" #include "serialize.hh" #include "serialize_stl.hh" #include "serialize_meta.hh" #include "memory.hh" #include "openmsx.hh" #include #include #include #include #include #include #include using std::string; using std::vector; using std::shared_ptr; using std::make_shared; namespace openmsx { static const byte SHIFT_MASK = 0x01; static const byte CTRL_MASK = 0x02; static const byte GRAPH_MASK = 0x04; static const byte CAPS_MASK = 0x08; static const byte CODE_MASK = 0x10; class KeyMatrixUpCmd : public RecordedCommand { public: KeyMatrixUpCmd(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, Keyboard& keyboard); virtual string execute(const vector& tokens, EmuTime::param time); virtual string help(const vector& tokens) const; private: Keyboard& keyboard; }; class KeyMatrixDownCmd : public RecordedCommand { public: KeyMatrixDownCmd(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, Keyboard& keyboard); virtual string execute(const vector& tokens, EmuTime::param time); virtual string help(const vector& tokens) const; private: Keyboard& keyboard; }; class MsxKeyEventQueue : public Schedulable { public: MsxKeyEventQueue(Scheduler& scheduler, Keyboard& keyboard); void process_asap(EmuTime::param time, const shared_ptr& event); void clear(); template void serialize(Archive& ar, unsigned version); private: // Schedulable virtual void executeUntil(EmuTime::param time, int userData); std::deque> eventQueue; Keyboard& keyboard; }; class KeyInserter : public RecordedCommand, public Schedulable { public: KeyInserter(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, Keyboard& keyboard); template void serialize(Archive& ar, unsigned version); private: void type(const string& str); void reschedule(EmuTime::param time); // Command virtual string execute(const vector& tokens, EmuTime::param time); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; // Schedulable virtual void executeUntil(EmuTime::param time, int userData); Keyboard& keyboard; string text_utf8; unsigned last; int lockKeysMask; bool releaseLast; bool oldCodeKanaLockOn; bool oldGraphLockOn; bool oldCapsLockOn; bool releaseBeforePress; unsigned typingFrequency; }; class CapsLockAligner : private EventListener, private Schedulable { public: CapsLockAligner(EventDistributor& eventDistributor, MSXEventDistributor& msxEventDistributor, Scheduler& scheduler, Keyboard& keyboard); virtual ~CapsLockAligner(); private: // EventListener virtual int signalEvent(const shared_ptr& event); // Schedulable virtual void executeUntil(EmuTime::param time, int userData); void alignCapsLock(EmuTime::param time); Keyboard& keyboard; EventDistributor& eventDistributor; MSXEventDistributor& msxEventDistributor; enum CapsLockAlignerStateType { MUST_ALIGN_CAPSLOCK, MUST_DISTRIBUTE_KEY_RELEASE, IDLE } state; }; class KeybDebuggable : public SimpleDebuggable { public: KeybDebuggable(MSXMotherBoard& motherBoard, Keyboard& keyboard); virtual byte read(unsigned address); virtual void write(unsigned address, byte value); private: Keyboard& keyboard; }; class KeyMatrixState : public StateChange { public: KeyMatrixState() {} // for serialize KeyMatrixState(EmuTime::param time, byte row_, byte press_, byte release_) : StateChange(time) , row(row_), press(press_), release(release_) { // disallow useless events assert((press != 0) || (release != 0)); // avoid confusion about what happens when some bits are both // set and reset (in other words: don't rely on order of and- // and or-operations) assert((press & release) == 0); } byte getRow() const { return row; } byte getPress() const { return press; } byte getRelease() const { return release; } template void serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("row", row); ar.serialize("press", press); ar.serialize("release", release); } private: byte row, press, release; }; REGISTER_POLYMORPHIC_CLASS(StateChange, KeyMatrixState, "KeyMatrixState"); static bool checkSDLReleasesCapslock() { const SDL_version* v = SDL_Linked_Version(); if (SDL_VERSIONNUM(v->major, v->minor, v->patch) < SDL_VERSIONNUM(1, 2, 14)) { // Feature was introduced in SDL 1.2.14. return false; } else { // Check whether feature was enabled by envvar. char *val = SDL_getenv("SDL_DISABLE_LOCK_KEYS"); return val && (strcmp(val, "1") == 0 || strcmp(val, "2") == 0); } } Keyboard::Keyboard(MSXMotherBoard& motherBoard, Scheduler& scheduler, CommandController& commandController_, EventDistributor& eventDistributor, MSXEventDistributor& msxEventDistributor_, StateChangeDistributor& stateChangeDistributor_, string_ref keyboardType, bool hasKP, bool hasYNKeys, bool keyGhosting_, bool keyGhostSGCprotected, bool codeKanaLocks_, bool graphLocks_) : Schedulable(scheduler) , commandController(commandController_) , msxEventDistributor(msxEventDistributor_) , stateChangeDistributor(stateChangeDistributor_) , keyMatrixUpCmd (make_unique( commandController, stateChangeDistributor, scheduler, *this)) , keyMatrixDownCmd(make_unique( commandController, stateChangeDistributor, scheduler, *this)) , keyTypeCmd(make_unique( commandController, stateChangeDistributor, scheduler, *this)) , capsLockAligner(make_unique( eventDistributor, msxEventDistributor, scheduler, *this)) , keyboardSettings(make_unique(commandController)) , msxKeyEventQueue(make_unique(scheduler, *this)) , keybDebuggable(make_unique(motherBoard, *this)) , unicodeKeymap(make_unique(keyboardType)) , hasKeypad(hasKP) , hasYesNoKeys(hasYNKeys) , keyGhosting(keyGhosting_) , keyGhostingSGCprotected(keyGhostSGCprotected) , codeKanaLocks(codeKanaLocks_) , graphLocks(graphLocks_) , sdlReleasesCapslock(checkSDLReleasesCapslock()) { // SDL version >= 1.2.14 releases caps-lock key when SDL_DISABLED_LOCK_KEYS // environment variable is already set in main.cc (because here it // would be too late) keysChanged = false; msxCapsLockOn = false; msxCodeKanaLockOn = false; msxGraphLockOn = false; msxmodifiers = 0xff; memset(keyMatrix, 255, sizeof(keyMatrix)); memset(cmdKeyMatrix, 255, sizeof(cmdKeyMatrix)); memset(userKeyMatrix, 255, sizeof(userKeyMatrix)); memset(hostKeyMatrix, 255, sizeof(hostKeyMatrix)); memset(dynKeymap, 0, sizeof(dynKeymap)); msxEventDistributor.registerEventListener(*this); stateChangeDistributor.registerListener(*this); // We do not listen for CONSOLE_OFF_EVENTS because rescanning the // keyboard can have unwanted side effects motherBoard.getReverseManager().registerKeyboard(*this); } Keyboard::~Keyboard() { stateChangeDistributor.unregisterListener(*this); msxEventDistributor.unregisterEventListener(*this); } const byte* Keyboard::getKeys() { if (keysChanged) { keysChanged = false; for (unsigned i = 0; i < NR_KEYROWS; ++i) { keyMatrix[i] = cmdKeyMatrix[i] & userKeyMatrix[i]; } if (keyGhosting) { doKeyGhosting(); } } return keyMatrix; } void Keyboard::transferHostKeyMatrix(const Keyboard& source) { // This mechanism exists to solve the following problem: // - play a game where the spacebar is constantly pressed (e.g. // Road Fighter) // - go back in time (press the reverse hotkey) while keeping the // spacebar pressed // - interrupt replay by pressing the cursor keys, still while // keeping spacebar pressed // At the moment replay is interrupted, we need to resynchronize the // msx keyboard with the host keyboard. In the past we assumed the host // keyboard had no keys pressed. But this is wrong in the above // scenario. Now we remember the state of the host keyboard and // transfer that to the new keyboard(s) that get created for reverese. // When replay is stopped we restore this host keyboard state, see // stopReplay(). for (unsigned row = 0; row < NR_KEYROWS; ++row) { hostKeyMatrix[row] = source.hostKeyMatrix[row]; } } /* Received an MSX event * Following events get processed: * OPENMSX_KEY_DOWN_EVENT * OPENMSX_KEY_UP_EVENT */ void Keyboard::signalEvent(const shared_ptr& event, EmuTime::param time) { EventType type = event->getType(); if ((type == OPENMSX_KEY_DOWN_EVENT) || (type == OPENMSX_KEY_UP_EVENT)) { // Ignore possible console on/off events: // we do not rescan the keyboard since this may lead to // an unwanted pressing of in MSX after typing // "set console off" in the console. msxKeyEventQueue->process_asap(time, event); } } void Keyboard::signalStateChange(const shared_ptr& event) { auto kms = dynamic_cast(event.get()); if (!kms) return; userKeyMatrix[kms->getRow()] &= ~kms->getPress(); userKeyMatrix[kms->getRow()] |= kms->getRelease(); keysChanged = true; // do ghosting at next getKeys() } void Keyboard::stopReplay(EmuTime::param time) { for (unsigned row = 0; row < NR_KEYROWS; ++row) { changeKeyMatrixEvent(time, row, hostKeyMatrix[row]); } msxmodifiers = 0xff; msxKeyEventQueue->clear(); memset(dynKeymap, 0, sizeof(dynKeymap)); } void Keyboard::pressKeyMatrixEvent(EmuTime::param time, byte row, byte press) { assert(press); if (((hostKeyMatrix[row] & press) == 0) && ((userKeyMatrix[row] & press) == 0)) { // Won't have any effect, ignore. return; } changeKeyMatrixEvent(time, row, hostKeyMatrix[row] & ~press); } void Keyboard::releaseKeyMatrixEvent(EmuTime::param time, byte row, byte release) { assert(release); if (((hostKeyMatrix[row] & release) == release) && ((userKeyMatrix[row] & release) == release)) { // Won't have any effect, ignore. // Test scenario: during replay, exit the openmsx console with // the 'toggle console' command. The 'enter,release' event will // end up here. But it shouldn't stop replay. return; } changeKeyMatrixEvent(time, row, hostKeyMatrix[row] | release); } void Keyboard::changeKeyMatrixEvent(EmuTime::param time, byte row, byte newValue) { // This method already updates hostKeyMatrix[], // userKeyMatrix[] will soon be updated via KeyMatrixState events. hostKeyMatrix[row] = newValue; byte diff = userKeyMatrix[row] ^ newValue; if (diff == 0) return; byte press = userKeyMatrix[row] & diff; byte release = newValue & diff; stateChangeDistributor.distributeNew(make_shared( time, row, press, release)); } bool Keyboard::processQueuedEvent(const Event& event, EmuTime::param time) { bool insertCodeKanaRelease = false; auto& keyEvent = checked_cast(event); bool down = event.getType() == OPENMSX_KEY_DOWN_EVENT; auto key = static_cast( int(keyEvent.getKeyCode()) & int(Keys::K_MASK)); if (down) { // TODO: refactor debug(...) method to expect a std::string and then adapt // all invocations of it to provide a properly formatted string, using the C++ // features for it. // Once that is done, debug(...) can pass the c_str() version of that string // to ad_printf(...) so that I don't have to make an explicit ad_printf(...) // invocation for each debug(...) invocation ad_printf("Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n", keyEvent.getUnicode(), keyEvent.getKeyCode(), Keys::getName(keyEvent.getKeyCode()).c_str()); debug("Key pressed, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n", keyEvent.getUnicode(), keyEvent.getKeyCode(), Keys::getName(keyEvent.getKeyCode()).c_str()); } else { ad_printf("Key released, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n", keyEvent.getUnicode(), keyEvent.getKeyCode(), Keys::getName(keyEvent.getKeyCode()).c_str()); debug("Key released, unicode: 0x%04x, keyCode: 0x%05x, keyName: %s\n", keyEvent.getUnicode(), keyEvent.getKeyCode(), Keys::getName(keyEvent.getKeyCode()).c_str()); } if (key == keyboardSettings->getDeadkeyHostKey(0) && keyboardSettings->getMappingMode().getEnum() == KeyboardSettings::CHARACTER_MAPPING) { processDeadKeyEvent(0, time, down); } else if (key == keyboardSettings->getDeadkeyHostKey(1) && keyboardSettings->getMappingMode().getEnum() == KeyboardSettings::CHARACTER_MAPPING) { processDeadKeyEvent(1, time, down); } else if (key == keyboardSettings->getDeadkeyHostKey(2) && keyboardSettings->getMappingMode().getEnum() == KeyboardSettings::CHARACTER_MAPPING) { processDeadKeyEvent(2, time, down); } else if (key == Keys::K_CAPSLOCK) { processCapslockEvent(time, down); } else if (key == keyboardSettings->getCodeKanaHostKey().getEnum()) { processCodeKanaChange(time, down); } else if (key == Keys::K_LALT) { processGraphChange(time, down); } else if (key == Keys::K_KP_ENTER) { processKeypadEnterKey(time, down); } else { insertCodeKanaRelease = processKeyEvent(time, down, keyEvent); } return insertCodeKanaRelease; } /* * Process a change (up or down event) of the CODE/KANA key * It presses or releases the key in the MSX keyboard matrix * and changes the kanalock state in case of a press */ void Keyboard::processCodeKanaChange(EmuTime::param time, bool down) { if (down) { msxCodeKanaLockOn = !msxCodeKanaLockOn; } updateKeyMatrix(time, down, 6, CODE_MASK); } /* * Process a change (up or down event) of the GRAPH key * It presses or releases the key in the MSX keyboard matrix * and changes the graphlock state in case of a press */ void Keyboard::processGraphChange(EmuTime::param time, bool down) { if (down) { msxGraphLockOn = !msxGraphLockOn; } updateKeyMatrix(time, down, 6, GRAPH_MASK); } /* * Process deadkey N by pressing or releasing the deadkey * at the correct location in the keyboard matrix */ void Keyboard::processDeadKeyEvent(unsigned n, EmuTime::param time, bool down) { UnicodeKeymap::KeyInfo deadkey = unicodeKeymap->getDeadkey(n); if (deadkey.keymask) { updateKeyMatrix(time, down, deadkey.row, deadkey.keymask); } } /* * Process a change event of the CAPSLOCK *STATUS*; * SDL up to version 1.2.13 sends a CAPSLOCK press event at the moment that * the host CAPSLOCK status goes 'on' and it sends the release event only when * the host CAPSLOCK status goes 'off'. However, the emulated MSX must see a * press and release event when CAPSLOCK status goes on and another press and * release event when it goes off again. This is achieved by pressing CAPSLOCK * key at the moment that the host CAPSLOCK status changes and releasing the * CAPSLOCK key shortly after (via a timed event) * * SDL as of version 1.2.14 can send a press and release event at the moment * that the user presses and releases the CAPS lock. Though, this changed * behaviour is only enabled when a special environment variable is set. * * This version of openMSX supports both behaviours; when SDL version is at * least 1.2.14, it will set the environment variable to trigger the new * behaviour and simply process the press and release events as they come in. * For older SDL versions, it will still treat each change as a press that must * be followed by a scheduled release event */ void Keyboard::processCapslockEvent(EmuTime::param time, bool down) { if (sdlReleasesCapslock) { debug("Changing CAPS lock state according to SDL request\n"); if (down) { msxCapsLockOn = !msxCapsLockOn; } updateKeyMatrix(time, down, 6, CAPS_MASK); } else { debug("Pressing CAPS lock and scheduling a release\n"); msxCapsLockOn = !msxCapsLockOn; updateKeyMatrix(time, true, 6, CAPS_MASK); setSyncPoint(time + EmuDuration::hz(10)); // 0.1s (in MSX time) } } void Keyboard::executeUntil(EmuTime::param time, int /*userData*/) { debug("Releasing CAPS lock\n"); updateKeyMatrix(time, false, 6, CAPS_MASK); } void Keyboard::processKeypadEnterKey(EmuTime::param time, bool down) { if (!hasKeypad && !keyboardSettings->getAlwaysEnableKeypad().getBoolean()) { // User entered on host keypad but this MSX model does not have one // Ignore the keypress/release return; } int row; byte mask; if (keyboardSettings->getKpEnterMode().getEnum() == KeyboardSettings::MSX_KP_COMMA) { row = 10; mask = 0x40; } else { row = 7; mask = 0x80; } updateKeyMatrix(time, down, row, mask); } /* * Process an SDL key press/release event. It concerns a * special key (e.g. SHIFT, UP, DOWN, F1, F2, ...) that can not * be unambiguously derived from a unicode character; * Map the SDL key to an equivalent MSX key press/release event */ void Keyboard::processSdlKey(EmuTime::param time, bool down, int key) { if (key < MAX_KEYSYM) { int row = keyTab[key] >> 4; byte mask = 1 << (keyTab[key] & 0xf); if (keyTab[key] == 0xff) assert(mask == 0); if ((row == 11) && !hasYesNoKeys) { // do not process row 11 if we have no Yes/No keys return; } if (mask) { updateKeyMatrix(time, down, row, mask); } } } /* * Update the MSX keyboard matrix */ void Keyboard::updateKeyMatrix(EmuTime::param time, bool down, int row, byte mask) { assert(mask); if (down) { pressKeyMatrixEvent(time, row, mask); if (row == 6) { // Keep track of the MSX modifiers (CTRL, GRAPH, CODE, SHIFT) // The MSX modifiers in row 6 of the matrix sometimes get // overruled by the unicode character processing, in // which case the unicode processing must be able to restore // them to the real key-combinations pressed by the user msxmodifiers &= ~(mask & 0x17); } } else { releaseKeyMatrixEvent(time, row, mask); if (row == 6) { msxmodifiers |= (mask & 0x17); } } } /* * Process an SDL key event; * Check if it is a special key, in which case it can be directly * mapped to the MSX matrix. * Otherwise, retrieve the unicode character value for the event * and map the unicode character to the key-combination that must * be pressed to generate the equivalent character on the MSX */ bool Keyboard::processKeyEvent(EmuTime::param time, bool down, const KeyEvent& keyEvent) { bool insertCodeKanaRelease = false; Keys::KeyCode keyCode = keyEvent.getKeyCode(); auto key = static_cast( int(keyCode) & int(Keys::K_MASK)); unsigned unicode; bool isOnKeypad = ( (key >= Keys::K_KP0 && key <= Keys::K_KP9) || (key == Keys::K_KP_PERIOD) || (key == Keys::K_KP_DIVIDE) || (key == Keys::K_KP_MULTIPLY) || (key == Keys::K_KP_MINUS) || (key == Keys::K_KP_PLUS)); if (isOnKeypad && !hasKeypad && !keyboardSettings->getAlwaysEnableKeypad().getBoolean()) { // User entered on host keypad but this MSX model does not have one // Ignore the keypress/release return false; } if (down) { if (/*___(userKeyMatrix[6] & 2) == 0 || */ isOnKeypad || keyboardSettings->getMappingMode().getEnum() == KeyboardSettings::KEY_MAPPING) { // /*CTRL-key is active,*/ user entered a key on numeric // keypad or the driver is in KEY mapping mode. // First /*two*/ option/*s*/ (/*CTRL key active,*/ keypad keypress) maps // to same unicode as some other key combinations (e.g. digit // on main keyboard or TAB/DEL) // Use unicode to handle the more common combination // and use direct matrix to matrix mapping for the exceptional // cases (/*CTRL+character or*/ numeric keypad usage) unicode = 0; #if defined(__APPLE__) } else if ((keyCode & (Keys::K_MASK | Keys::KM_META)) == (Keys::K_I | Keys::KM_META)) { // Apple keyboards don't have an Insert key, use Cmd+I as an alternative. keyCode = key = Keys::K_INSERT; unicode = 0; #endif } else { unicode = keyEvent.getUnicode(); if ((unicode < 0x20) || ((0x7F <= unicode) && (unicode < 0xA0))) { // Control character in C0 or C1 range. // Use SDL's interpretation instead. unicode = 0; } else if ((0xE000 <= unicode) && (unicode < 0xF900)) { // Code point in Private Use Area: undefined by Unicode, // so we rely on SDL's interpretation instead. // For example the Mac's cursor keys are in this range. unicode = 0; } } if (key < MAX_KEYSYM) { // Remember which unicode character is currently derived // from this SDL key. It must be stored here (during key-press) // because during key-release SDL never returns the unicode // value (it always returns the value 0). But we must know // the unicode value in order to be able to perform the correct // key-combination-release in the MSX dynKeymap[key] = unicode; } else { // Unexpectedly high key-code. Can't store the unicode // character for this key. Instead directly treat the key // via matrix to matrix mapping unicode = 0; } if (unicode == 0) { // It was an ambiguous key (numeric key-pad, CTRL+character) // or a special key according to SDL (like HOME, INSERT, etc) // or a first keystroke of a composed key // (e.g. altr-gr + = on azerty keyboard) or driver is in // direct SDL mapping mode: // Perform direct SDL matrix to MSX matrix mapping // But only when it is not a first keystroke of a // composed key if ((keyCode & Keys::KM_MODE) == 0) { processSdlKey(time, down, key); } } else { // It is a unicode character; map it to the right key-combination insertCodeKanaRelease = pressUnicodeByUser(time, unicode, true); } } else { // key was released #if defined(__APPLE__) if ((keyCode & (Keys::K_MASK | Keys::KM_META)) == (Keys::K_I | Keys::KM_META)) { keyCode = key = Keys::K_INSERT; } #endif if (key < MAX_KEYSYM) { unicode = dynKeymap[key]; // Get the unicode that was derived from this key } else { unicode = 0; } if (unicode == 0) { // It was a special key, perform matrix to matrix mapping // But only when it is not a first keystroke of a // composed key if ((keyCode & Keys::KM_MODE) == 0) { processSdlKey(time, down, key); } } else { // It was a unicode character; map it to the right key-combination pressUnicodeByUser(time, unicode, false); } } return insertCodeKanaRelease; } void Keyboard::doKeyGhosting() { // This routine enables keyghosting as seen on a real MSX // // If on a real MSX in the keyboardmatrix the // real buttons are pressed as in the left matrix // then the matrix to the // 10111111 right will be read by 10110101 // 11110101 because of the simple 10110101 // 10111101 electrical connections 10110101 // that are established by // the closed switches // However, some MSX models have protection against // key-ghosting for SHIFT, GRAPH and CODE keys // On those models, SHIFT, GRAPH and CODE are // connected to row 6 via a diode. It prevents that // SHIFT, GRAPH and CODE get ghosted to another // row. bool changedSomething; do { changedSomething = false; for (unsigned i = 0; i < NR_KEYROWS - 1; i++) { byte row1 = keyMatrix[i]; for (unsigned j = i + 1; j < NR_KEYROWS; j++) { byte row2 = keyMatrix[j]; if ((row1 != row2) && ((row1 | row2) != 0xff)) { byte rowIold = keyMatrix[i]; byte rowJold = keyMatrix[j]; if (keyGhostingSGCprotected && i == 6) { keyMatrix[i] = row1 & row2; keyMatrix[j] = (row1 | 0x15) & row2; row1 &= row2; } else if (keyGhostingSGCprotected && j == 6) { keyMatrix[i] = row1 & (row2 | 0x15); keyMatrix[j] = row1 & row2; row1 &= (row2 | 0x15); } else { // not same and some common zero's // --> inherit other zero's byte newRow = row1 & row2; keyMatrix[i] = newRow; keyMatrix[j] = newRow; row1 = newRow; } if (rowIold != keyMatrix[i] || rowJold != keyMatrix[j]) { changedSomething = true; } } } } } while (changedSomething); } string Keyboard::processCmd(const vector& tokens, bool up) { if (tokens.size() != 3) { throw SyntaxError(); } unsigned row, mask; if (!StringOp::stringToUint(tokens[1], row) || (row >= NR_KEYROWS)) { throw CommandException("Invalid row"); } if (!StringOp::stringToUint(tokens[2], mask) || (mask >= 256)) { throw CommandException("Invalid mask"); } if (up) { cmdKeyMatrix[row] |= mask; } else { cmdKeyMatrix[row] &= ~mask; } keysChanged = true; return ""; } /* * This routine processes unicode characters. It maps a unicode character * to the correct key-combination on the MSX. * * There are a few caveats with respect to the MSX and Host modifier keys * that you must be aware about if you want to understand why the routine * works as it works. * * Row 6 of the MSX keyboard matrix contains the MSX modifier keys: * CTRL, CODE, GRAPH and SHIFT * * The SHIFT key is also a modifier key on the host machine. However, the * SHIFT key behaviour can differ between HOST and MSX for all 'special' * characters (anything but A-Z). * For example, on AZERTY host keyboard, user presses SHIFT+& to make the '1' * On MSX QWERTY keyboard, the same key-combination leads to '!'. * So this routine must not only PRESS the SHIFT key when required according * to the unicode mapping table but it must also RELEASE the SHIFT key for all * these special keys when the user PRESSES the key/character. * * On the other hand, for A-Z, this routine must not touch the SHIFT key at all. * Otherwise it might give strange behaviour when CAPS lock is on (which also * acts as a key-modifier for A-Z). The routine can rely on the fact that * SHIFT+A-Z behaviour is the same on all host and MSX keyboards. It is * approximately the only part of keyboards that is de-facto standardized :-) * * For the other modifiers (CTRL, CODE and GRAPH), the routine must be able to * PRESS them when required but there is no need to RELEASE them during * character press. On the contrary; the host keys that map to CODE and GRAPH * do not work as modifiers on the host itself, so if the routine would release * them, it would give wrong result. * For example, 'ALT-A' on Host will lead to unicode character 'a', just like * only pressing the 'A' key. The MSX however must know about the difference. * * As a reminder: here is the build-up of row 6 of the MSX key matrix * 7 6 5 4 3 2 1 0 * row 6 | F3 | F2 | F1 | code| caps|graph| ctrl|shift| */ bool Keyboard::pressUnicodeByUser(EmuTime::param time, unsigned unicode, bool down) { bool insertCodeKanaRelease = false; UnicodeKeymap::KeyInfo keyInfo = unicodeKeymap->get(unicode); if (keyInfo.keymask == 0) { return insertCodeKanaRelease; } if (down) { if (codeKanaLocks && keyboardSettings->getAutoToggleCodeKanaLock().getBoolean() && msxCodeKanaLockOn != ((keyInfo.modmask & CODE_MASK) == CODE_MASK) && keyInfo.row < 6) { // only toggle CODE lock for 'normal' characters // Code Kana locks, is in wrong state and must be auto-toggled: // Toggle it by pressing the lock key and scheduling a // release event msxCodeKanaLockOn = !msxCodeKanaLockOn; pressKeyMatrixEvent(time, 6, CODE_MASK); insertCodeKanaRelease = true; } else { // Press the character key and related modifiers // Ignore the CODE key in case that Code Kana locks // (e.g. do not press it). // Ignore the GRAPH key in case that Graph locks // Always ignore CAPSLOCK mask (assume that user will // use real CAPS lock to switch/ between hiragana and // katanana on japanese model) assert(keyInfo.keymask); pressKeyMatrixEvent(time, keyInfo.row, keyInfo.keymask); byte modmask = keyInfo.modmask & ~CAPS_MASK; if (codeKanaLocks) modmask &= ~CODE_MASK; if (graphLocks) modmask &= ~GRAPH_MASK; if (('A' <= unicode && unicode <= 'Z') || ('a' <= unicode && unicode <= 'z')) { // for a-z and A-Z, leave shift unchanged, this to cater // for difference in behaviour between host and emulated // machine with respect to how the combination of CAPSLOCK // and shift-key is interpreted for these characters. // Note that other modifiers are only pressed, never released byte press = modmask & ~SHIFT_MASK; if (press) { pressKeyMatrixEvent(time, 6, press); } } else { // for other keys, set shift according to modmask // so also release shift when required (other // modifiers are only pressed, never released) byte newRow = (userKeyMatrix[6] | SHIFT_MASK) & ~modmask; changeKeyMatrixEvent(time, 6, newRow); } } } else { assert(keyInfo.keymask); releaseKeyMatrixEvent(time, keyInfo.row, keyInfo.keymask); // Do not simply unpress graph, ctrl, code and shift but // restore them to the values currently pressed by the user byte mask = SHIFT_MASK | CTRL_MASK; if (!codeKanaLocks) mask |= CODE_MASK; if (!graphLocks) mask |= GRAPH_MASK; byte newRow = userKeyMatrix[6]; newRow &= msxmodifiers | ~mask; newRow |= msxmodifiers & mask; changeKeyMatrixEvent(time, 6, newRow); } keysChanged = true; return insertCodeKanaRelease; } /* * Press an ASCII character. It is used by the 'Insert characters' * function that is exposed to the console. * The characters are inserted in a separate keyboard matrix, to prevent * interference with the keypresses of the user on the MSX itself */ int Keyboard::pressAscii(unsigned unicode, bool down) { int releaseMask = 0; UnicodeKeymap::KeyInfo keyInfo = unicodeKeymap->get(unicode); byte modmask = keyInfo.modmask & (~CAPS_MASK); // ignore CAPSLOCK mask; if (codeKanaLocks) { modmask &= (~CODE_MASK); // ignore CODE mask if CODE locks } if (graphLocks) { modmask &= (~GRAPH_MASK); // ignore GRAPH mask if GRAPH locks } if (down) { if (codeKanaLocks && msxCodeKanaLockOn != ((keyInfo.modmask & CODE_MASK) == CODE_MASK) && keyInfo.row < 6) { // only toggle CODE lock for 'normal' characters debug("Toggling CODE/KANA lock\n"); msxCodeKanaLockOn = !msxCodeKanaLockOn; cmdKeyMatrix[6] &= (~CODE_MASK); releaseMask = CODE_MASK; } if (graphLocks && msxGraphLockOn != ((keyInfo.modmask & GRAPH_MASK) == GRAPH_MASK) && keyInfo.row < 6) { // only toggle GRAPH lock for 'normal' characters debug("Toggling GRAPH lock\n"); msxGraphLockOn = !msxGraphLockOn; cmdKeyMatrix[6] &= (~GRAPH_MASK); releaseMask |= GRAPH_MASK; } if (msxCapsLockOn != ((keyInfo.modmask & CAPS_MASK) == CAPS_MASK) && keyInfo.row < 6) { // only toggle CAPS lock for 'normal' characters debug("Toggling CAPS lock\n"); msxCapsLockOn = !msxCapsLockOn; cmdKeyMatrix[6] &= (~CAPS_MASK); releaseMask |= CAPS_MASK; } if (releaseMask == 0) { debug("Key pasted, unicode: 0x%04x, row: %02d, mask: %02x, modmask: %02x\n", unicode, keyInfo.row, keyInfo.keymask, modmask); cmdKeyMatrix[keyInfo.row] &= ~keyInfo.keymask; cmdKeyMatrix[6] &= ~modmask; } } else { cmdKeyMatrix[keyInfo.row] |= keyInfo.keymask; cmdKeyMatrix[6] |= modmask; } keysChanged = true; return releaseMask; } /* * Press a lock key. It is used by the 'Insert characters' * function that is exposed to the console. * The characters are inserted in a separate keyboard matrix, to prevent * interference with the keypresses of the user on the MSX itself */ void Keyboard::pressLockKeys(int lockKeysMask, bool down) { if (down) { // press CAPS and/or CODE/KANA lock key cmdKeyMatrix[6] &= (~lockKeysMask); } else { // release CAPS and/or CODE/KANA lock key cmdKeyMatrix[6] |= lockKeysMask; } keysChanged = true; } /* * Check if there are common keys in the MSX matrix for * two different unicodes. * It is used by the 'insert keys' function to determine if it has to wait for * a short while after releasing a key (to enter a certain character) before * pressing the next key (to enter the next character) */ bool Keyboard::commonKeys(unsigned unicode1, unsigned unicode2) { // get row / mask of key (note: ignore modifier mask) UnicodeKeymap::KeyInfo keyInfo1 = unicodeKeymap->get(unicode1); UnicodeKeymap::KeyInfo keyInfo2 = unicodeKeymap->get(unicode2); return ((keyInfo1.row == keyInfo2.row) && (keyInfo1.keymask & keyInfo2.keymask)); } void Keyboard::debug(const char* format, ...) { if (keyboardSettings->getTraceKeyPresses().getBoolean()) { va_list args; va_start(args, format); vfprintf(stderr, format, args); va_end(args); } } // class KeyMatrixUpCmd KeyMatrixUpCmd::KeyMatrixUpCmd(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, Keyboard& keyboard_) : RecordedCommand(commandController, stateChangeDistributor, scheduler, "keymatrixup") , keyboard(keyboard_) { } string KeyMatrixUpCmd::execute(const vector& tokens, EmuTime::param /*time*/) { return keyboard.processCmd(tokens, true); } string KeyMatrixUpCmd::help(const vector& /*tokens*/) const { static const string helpText = "keymatrixup release a key in the keyboardmatrix\n"; return helpText; } // class KeyMatrixDownCmd KeyMatrixDownCmd::KeyMatrixDownCmd(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, Keyboard& keyboard_) : RecordedCommand(commandController, stateChangeDistributor, scheduler, "keymatrixdown") , keyboard(keyboard_) { } string KeyMatrixDownCmd::execute(const vector& tokens, EmuTime::param /*time*/) { return keyboard.processCmd(tokens, false); } string KeyMatrixDownCmd::help(const vector& /*tokens*/) const { static const string helpText= "keymatrixdown press a key in the keyboardmatrix\n"; return helpText; } // class MsxKeyEventQueue MsxKeyEventQueue::MsxKeyEventQueue(Scheduler& scheduler, Keyboard& keyboard_) : Schedulable(scheduler) , keyboard(keyboard_) { } void MsxKeyEventQueue::process_asap(EmuTime::param time, const shared_ptr& event) { bool processImmediately = eventQueue.empty(); eventQueue.push_back(event); if (processImmediately) { executeUntil(time, 0); } } void MsxKeyEventQueue::clear() { eventQueue.clear(); removeSyncPoint(); } void MsxKeyEventQueue::executeUntil(EmuTime::param time, int /*userData*/) { // Get oldest event from the queue and process it shared_ptr event = eventQueue.front(); bool insertCodeKanaRelease = keyboard.processQueuedEvent(*event, time); if (insertCodeKanaRelease) { // The processor pressed the CODE/KANA key // Schedule a CODE/KANA release event, to be processed // before any of the other events in the queue eventQueue.push_front(make_shared( keyboard.keyboardSettings->getCodeKanaHostKey().getEnum())); } else { // The event has been completely processed. Delete it from the queue if (!eventQueue.empty()) { eventQueue.pop_front(); } else { // it's possible clear() has been called // (indirectly from keyboard.processQueuedEvent()) } } if (!eventQueue.empty()) { // There are still events. Process them in 1/15s from now setSyncPoint(time + EmuDuration::hz(15)); } } // class KeyInserter KeyInserter::KeyInserter(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, Keyboard& keyboard_) : RecordedCommand(commandController, stateChangeDistributor, scheduler, "type") , Schedulable(scheduler) , keyboard(keyboard_) , lockKeysMask(0) , releaseLast(false) { // avoid UMR last = 0; oldCodeKanaLockOn = false; oldGraphLockOn = false; oldCapsLockOn = false; releaseBeforePress = false; typingFrequency = 15; } string KeyInserter::execute(const vector& tokens, EmuTime::param /*time*/) { if (tokens.size() < 2) { throw SyntaxError(); } releaseBeforePress = false; typingFrequency = 15; // for full backwards compatibility: one option means type it... if (tokens.size() == 2) { type(tokens[1]); return ""; } vector arguments; for (unsigned i = 1; i < tokens.size(); ++i) { const string token = tokens[i]; if (token == "-release") { releaseBeforePress = true; } else if (token == "-freq") { if (++i == tokens.size()) { throw CommandException("Missing argument"); } if (!StringOp::stringToUint(tokens[i], typingFrequency) || (typingFrequency == 0)) { throw CommandException("Wrong argument for -freq (should be a positive number)"); } } else { arguments.push_back(token); } } if (arguments.size() != 1) throw SyntaxError(); type(arguments[0]); return ""; } string KeyInserter::help(const vector& /*tokens*/) const { static const string helpText = "Type a string in the emulated MSX.\n" \ "Use -release to make sure the keys are always released before typing new ones (necessary for some game input routines, but in general, this means typing is twice as slow).\n" \ "Use -freq to tweak how fast typing goes and how long the keys will be pressed (and released in case -release was used). Keys will be typed at the given frequency and will remain pressed/released for 1/freq seconds"; return helpText; } void KeyInserter::tabCompletion(vector& tokens) const { vector options; if (find(tokens.begin(), tokens.end(), "-release") == tokens.end()) { options.push_back("-release"); } if (find(tokens.begin(), tokens.end(), "-freq") == tokens.end()) { options.push_back("-freq"); } completeString(tokens, options); } void KeyInserter::type(const string& str) { if (str.empty()) { return; } oldCodeKanaLockOn = keyboard.msxCodeKanaLockOn; oldGraphLockOn = keyboard.msxGraphLockOn; oldCapsLockOn = keyboard.msxCapsLockOn; if (text_utf8.empty()) { reschedule(getCurrentTime()); } text_utf8 += str; } void KeyInserter::executeUntil(EmuTime::param time, int /*userData*/) { if (lockKeysMask != 0) { // release CAPS and/or Code/Kana Lock keys keyboard.pressLockKeys(lockKeysMask, false); } if (releaseLast) { keyboard.pressAscii(last, false); // release previous character } if (text_utf8.empty()) { releaseLast = false; lockKeysMask = 0; if (oldCodeKanaLockOn != keyboard.msxCodeKanaLockOn) { keyboard.debug("Restoring CODE/KANA lock\n"); lockKeysMask = CODE_MASK; keyboard.msxCodeKanaLockOn = !keyboard.msxCodeKanaLockOn; } if (oldGraphLockOn != keyboard.msxGraphLockOn) { keyboard.debug("Restoring GRAPH lock\n"); lockKeysMask |= GRAPH_MASK; keyboard.msxGraphLockOn = !keyboard.msxGraphLockOn; } if (oldCapsLockOn != keyboard.msxCapsLockOn) { keyboard.debug("Restoring CAPS lock\n"); lockKeysMask |= CAPS_MASK; keyboard.msxCapsLockOn = !keyboard.msxCapsLockOn; } if (lockKeysMask != 0) { // press CAPS, GRAPH and/or Code/Kana Lock keys keyboard.pressLockKeys(lockKeysMask, true); reschedule(time); } return; } try { auto it = text_utf8.begin(); unsigned current = utf8::next(it, text_utf8.end()); if (releaseLast == true && (releaseBeforePress || keyboard.commonKeys(last, current))) { // There are common keys between previous and current character // Do not immediately press again but give MSX the time to notice // that the keys have been released releaseLast = false; } else { // All keys in current char differ from previous char. The new keys // can immediately be pressed lockKeysMask = keyboard.pressAscii(current, true); if (lockKeysMask == 0) { last = current; releaseLast = true; text_utf8.erase(text_utf8.begin(), it); } if (releaseBeforePress) releaseLast = true; } reschedule(time); } catch (std::exception&) { // utf8 encoding error text_utf8.clear(); } } void KeyInserter::reschedule(EmuTime::param time) { setSyncPoint(time + EmuDuration::hz(typingFrequency)); } /* * class CapsLockAligner * * It is used to align MSX CAPS lock status with the host CAPS lock status * during the reset of the MSX or after the openMSX window regains focus. * * It listens to the 'BOOT' event and schedules the real alignment * 2 seconds later. Reason is that it takes a while before the MSX * reset routine starts monitoring the MSX keyboard. * * For focus regain, the alignment is done immediately. */ CapsLockAligner::CapsLockAligner(EventDistributor& eventDistributor_, MSXEventDistributor& msxEventDistributor_, Scheduler& scheduler, Keyboard& keyboard_) : Schedulable(scheduler) , keyboard(keyboard_) , eventDistributor(eventDistributor_) , msxEventDistributor(msxEventDistributor_) { state = IDLE; eventDistributor.registerEventListener(OPENMSX_BOOT_EVENT, *this); eventDistributor.registerEventListener(OPENMSX_FOCUS_EVENT, *this); } CapsLockAligner::~CapsLockAligner() { eventDistributor.unregisterEventListener(OPENMSX_FOCUS_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_BOOT_EVENT, *this); } int CapsLockAligner::signalEvent(const shared_ptr& event) { if (state == IDLE) { EmuTime::param time = getCurrentTime(); EventType type = event->getType(); if (type == OPENMSX_FOCUS_EVENT) { alignCapsLock(time); } else if (type == OPENMSX_BOOT_EVENT) { state = MUST_ALIGN_CAPSLOCK; setSyncPoint(time + EmuDuration::sec(2)); // 2s (MSX time) } else { UNREACHABLE; } } return 0; } void CapsLockAligner::executeUntil(EmuTime::param time, int /*userData*/) { switch (state) { case MUST_ALIGN_CAPSLOCK: alignCapsLock(time); break; case MUST_DISTRIBUTE_KEY_RELEASE: { assert(keyboard.sdlReleasesCapslock); auto event = make_shared(Keys::K_CAPSLOCK); msxEventDistributor.distributeEvent(event, time); state = IDLE; break; } default: UNREACHABLE; } } /* * Align MSX caps lock state with host caps lock state * WARNING: This function assumes that the MSX will see and * process the caps lock key press. * If MSX misses the key press for whatever reason (e.g. * interrupts are disabled), the caps lock state in this * module will mismatch with the real MSX caps lock state * TODO: Find a solution for the above problem. For example by monitoring * the MSX caps-lock LED state. */ void CapsLockAligner::alignCapsLock(EmuTime::param time) { bool hostCapsLockOn = ((SDL_GetModState() & KMOD_CAPS) != 0); if (keyboard.msxCapsLockOn != hostCapsLockOn) { keyboard.debug("Resyncing host and MSX CAPS lock\n"); // note: send out another event iso directly calling // processCapslockEvent() because we want this to be recorded auto event = make_shared(Keys::K_CAPSLOCK); msxEventDistributor.distributeEvent(event, time); if (keyboard.sdlReleasesCapslock) { keyboard.debug("Sending fake CAPS release\n"); state = MUST_DISTRIBUTE_KEY_RELEASE; setSyncPoint(time + EmuDuration::hz(10)); // 0.1s (MSX time) } else { state = IDLE; } } else { state = IDLE; } } // class KeybDebuggable KeybDebuggable::KeybDebuggable(MSXMotherBoard& motherBoard, Keyboard& keyboard_) : SimpleDebuggable(motherBoard, "keymatrix", "MSX Keyboard Matrix", Keyboard::NR_KEYROWS) , keyboard(keyboard_) { } byte KeybDebuggable::read(unsigned address) { return keyboard.getKeys()[address]; } void KeybDebuggable::write(unsigned /*address*/, byte /*value*/) { // ignore } template void KeyInserter::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("text", text_utf8); ar.serialize("last", last); ar.serialize("lockKeysMask", lockKeysMask); ar.serialize("releaseLast", releaseLast); ar.serialize("oldCodeKanaLockOn", oldCodeKanaLockOn); ar.serialize("oldGraphLockOn", oldGraphLockOn); ar.serialize("oldCapsLockOn", oldCapsLockOn); } // version 1: Initial version: {userKeyMatrix, dynKeymap, msxmodifiers, // msxKeyEventQueue} was intentionally not serialized. The reason // was that after a loadstate, you want the MSX keyboard to reflect // the state of the host keyboard. So any pressed MSX keys from the // time the savestate was created are cleared. // version 2: For reverse-replay it is important that snapshots contain the // full state of the MSX keyboard, so now we do serialize it. // TODO Is the assumption in version 1 correct (clear keyb state on load)? // If it is still useful for 'regular' loadstate, then we could implement // it by explicitly clearing the keyb state from the actual loadstate // command. (But let's only do this when experience shows it's really // better). template void Keyboard::serialize(Archive& ar, unsigned version) { ar.serialize("keyTypeCmd", *keyTypeCmd); ar.serialize("cmdKeyMatrix", cmdKeyMatrix); ar.serialize("msxCapsLockOn", msxCapsLockOn); ar.serialize("msxCodeKanaLockOn", msxCodeKanaLockOn); ar.serialize("msxGraphLockOn", msxGraphLockOn); if (ar.versionAtLeast(version, 2)) { ar.serialize("userKeyMatrix", userKeyMatrix); ar.serialize("dynKeymap", dynKeymap); ar.serialize("msxmodifiers", msxmodifiers); ar.serialize("msxKeyEventQueue", *msxKeyEventQueue); } // don't serialize hostKeyMatrix if (ar.isLoader()) { // force recalculation of keyMatrix // (from cmdKeyMatrix and userKeyMatrix) keysChanged = true; } } INSTANTIATE_SERIALIZE_METHODS(Keyboard); template void MsxKeyEventQueue::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); // serialization of deque> is not directly // supported by the serialization framework (main problem is the // constness, collections of shared_ptr to polymorhpic objects are // not a problem). Worked around this by serializing the events in // ascii format. (In all practical cases this queue will anyway be // empty or contain very few elements). //ar.serialize("eventQueue", eventQueue); vector eventStrs; if (!ar.isLoader()) { for (auto& e : eventQueue) { eventStrs.push_back(e->toString()); } } ar.serialize("eventQueue", eventStrs); if (ar.isLoader()) { assert(eventQueue.empty()); for (auto& s : eventStrs) { eventQueue.push_back(InputEventFactory::createInputEvent(s)); } } } INSTANTIATE_SERIALIZE_METHODS(MsxKeyEventQueue); /** Keyboard bindings ****************************************/ // MSX Key-Matrix table // // row/bit 7 6 5 4 3 2 1 0 // +-----+-----+-----+-----+-----+-----+-----+-----+ // 0 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | // 1 | ; | ] | [ | \ | = | - | 9 | 8 | // 2 | B | A | Acc | / | . | , | ` | ' | // 3 | J | I | H | G | F | E | D | C | // 4 | R | Q | P | O | N | M | L | K | // 5 | Z | Y | X | W | V | U | T | S | // 6 | F3 | F2 | F1 | code| caps|graph| ctrl|shift| // 7 | ret |selec| bs | stop| tab | esc | F5 | F4 | // 8 |right| down| up | left| del | ins | hom |space| // 9 | 4 | 3 | 2 | 1 | 0 | / | + | * | // 10 | . | , | - | 9 | 8 | 7 | 6 | 5 | // 11 | | | | | 'NO'| |'YES'| | // +-----+-----+-----+-----+-----+-----+-----+-----+ // Mapping from SDL keys to MSX keys static const byte x = 0xff; const byte Keyboard::keyTab[MAX_KEYSYM] = { // 0 1 2 3 4 5 6 7 8 9 a b c d e f x , x , x , x , x , x , x , x ,0x75,0x73, x , x , x ,0x77, x , x , //000 x , x , x , x , x , x , x , x , x , x , x ,0x72, x , x , x , x , //010 0x80, x , x , x , x , x , x ,0x20, x , x , x , x ,0x22,0x12,0x23,0x24, //020 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x10,0x11, x ,0x17, x ,0x13, x , x , //030 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //040 x ,0x84,0x85,0x87,0x86, x , x , x , x , x , x ,0x15,0x14,0x16, x , x , //050 0x21,0x26,0x27,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x40,0x41,0x42,0x43,0x44, //060 0x45,0x46,0x47,0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57, x , x , x ,0x62,0x83, //070 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //080 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //090 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0A0 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0B0 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0C0 x , x , x , x , x , x , x , x ,0x81, x , x , x , x , x , x , x , //0D0 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0E0 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //0F0 0x93,0x94,0x95,0x96,0x97,0xA0,0xA1,0xA2,0xA3,0xA4,0xA7,0x92,0x90,0xA5,0x91,0xA6, //100 x ,0x85,0x86,0x87,0x84,0x82,0x81, x , x , x ,0x65,0x66,0x67,0x70,0x71, x , //110 0x76,0x74, x , x , x , x , x , x , x , x , x , x , x ,0x63, x ,0x60, //120 0x60,0x25,0x61,0x64,0x62,0xB3,0xB1,0xB3,0xB1,0xB1,0xB3, x , x , x , x , x , //130 x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , x , //140 }; } // namespace openmsx openmsx-0.10.0/src/DynamicClock.cc0000644000175000017500000000055612262345041017510 0ustar manuelmanuel00000000000000#include "DynamicClock.hh" #include "serialize.hh" namespace openmsx { template void DynamicClock::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("lastTick", lastTick); unsigned freq = getFreq(); ar.serialize("freq", freq); if (ar.isLoader()) setFreq(freq); } INSTANTIATE_SERIALIZE_METHODS(DynamicClock); } // namespace openmsx openmsx-0.10.0/src/MSXDeviceSwitch.cc0000644000175000017500000000434312262345041020117 0ustar manuelmanuel00000000000000#include "MSXDeviceSwitch.hh" #include "MSXSwitchedDevice.hh" #include "MSXCPUInterface.hh" #include "MSXException.hh" #include "StringOp.hh" #include "serialize.hh" #include namespace openmsx { MSXDeviceSwitch::MSXDeviceSwitch(const DeviceConfig& config) : MSXDevice(config) { for (int i = 0; i < 256; ++i) { devices[i] = nullptr; } count = 0; selected = 0; } MSXDeviceSwitch::~MSXDeviceSwitch() { for (int i = 0; i < 256; ++i) { // all devices must be unregistered assert(!devices[i]); } assert(count == 0); } void MSXDeviceSwitch::registerDevice(byte id, MSXSwitchedDevice* device) { if (devices[id]) { // TODO implement multiplexing throw MSXException(StringOp::Builder() << "Already have a switched device with id " << int(id)); } devices[id] = device; if (count == 0) { for (byte port = 0x40; port < 0x50; ++port) { getCPUInterface().register_IO_In (port, this); getCPUInterface().register_IO_Out(port, this); } } ++count; } void MSXDeviceSwitch::unregisterDevice(byte id) { --count; if (count == 0) { for (byte port = 0x40; port < 0x50; ++port) { getCPUInterface().unregister_IO_Out(port, this); getCPUInterface().unregister_IO_In (port, this); } } assert(devices[id]); devices[id] = nullptr; } bool MSXDeviceSwitch::hasRegisteredDevices() const { return count != 0; } void MSXDeviceSwitch::reset(EmuTime::param /*time*/) { selected = 0; } byte MSXDeviceSwitch::readIO(word port, EmuTime::param time) { if (devices[selected]) { return devices[selected]->readSwitchedIO(port, time); } else { return 0xFF; } } byte MSXDeviceSwitch::peekIO(word port, EmuTime::param time) const { if (devices[selected]) { return devices[selected]->peekSwitchedIO(port, time); } else { return 0xFF; } } void MSXDeviceSwitch::writeIO(word port, byte value, EmuTime::param time) { if ((port & 0x0F) == 0x00) { selected = value; } else if (devices[selected]) { devices[selected]->writeSwitchedIO(port, value, time); } else { // ignore } } template void MSXDeviceSwitch::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("selected", selected); } INSTANTIATE_SERIALIZE_METHODS(MSXDeviceSwitch); } // namespace openmsx openmsx-0.10.0/src/laserdisc/0000755000175000017500000000000012262345041016604 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/laserdisc/node.mk0000644000175000017500000000026112262345041020061 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR_$(COMPONENT_LASERDISC):= \ OggReader LaserdiscPlayerCLI \ LaserdiscPlayer PioneerLDControl yuv2rgb HDR_ONLY:= include build/node-end.mk openmsx-0.10.0/src/laserdisc/PioneerLDControl.cc0000644000175000017500000001257412262345041022306 0ustar manuelmanuel00000000000000#include "PioneerLDControl.hh" #include "Rom.hh" #include "CacheLine.hh" #include "serialize.hh" #include "LaserdiscPlayer.hh" #include "MSXPPI.hh" #include "MSXException.hh" #include "VDP.hh" #include "memory.hh" namespace openmsx { /* * Laserdisc Control: there are three bits involved here. There are three * bits/connections involved. * - EXTACK (from Laserdisc -> MSX) will remain low for a while to acknowledge * it has received a command and is executing * - EXTCONTROL (from MSX -> Laserdisc) one bit information which is used for * sending commands * - PULSE (internal to MSX) 8175.5hz signal which used by software to * create the right pulses for communicating with the Laserdisc over * EXTCONTROL. * * Sound Muting: left and right audio channels from Laserdisc input can * be muted independently. After reset or startup both channels are muted. * The left muting is controlled by bit 7 of 0x7fff; set is not muted, * cleared is muted. If this bit changed from 0->1 (rising edge triggered * and inverted) then bit 4 of register C of PPI switches L muting; set * for muting disabled, clear for muting enabled. * * Cassette Input: If the motor relay is OFF then audio on the R channel * ends up in the PSG (regardless of muting); if the motor relay is ON * then normal tape input is used. */ PioneerLDControl::PioneerLDControl(const DeviceConfig& config) : MSXDevice(config) , rom(make_unique(getName() + " ROM", "rom", config)) , clock(EmuTime::zero) , irq(getMotherBoard(), "PioneerLDControl.IRQdisplayoff") , videoEnabled(false) { if (config.getChildDataAsBool("laserdisc", true)) { laserdisc = make_unique( getHardwareConfig(), *this); } reset(getCurrentTime()); } void PioneerLDControl::init() { MSXDevice::init(); const auto& references = getReferences(); ppi = references.size() >= 1 ? dynamic_cast(references[0]) : nullptr; if (!ppi) { throw MSXException("Invalid PioneerLDControl configuration: " "need reference to PPI device."); } vdp = references.size() == 2 ? dynamic_cast(references[1]) : nullptr; if (!vdp) { throw MSXException("Invalid PioneerLDControl configuration: " "need reference to VDP device."); } } PioneerLDControl::~PioneerLDControl() { } void PioneerLDControl::reset(EmuTime::param time) { mutel = muter = true; superimposing = false; extint = false; irq.reset(); if (laserdisc) laserdisc->setMuting(mutel, muter, time); } byte PioneerLDControl::readMem(word address, EmuTime::param time) { byte val = PioneerLDControl::peekMem(address, time); if (address == 0x7fff) { extint = false; if (irq.getState()) { irq.reset(); } } return val; } byte PioneerLDControl::peekMem(word address, EmuTime::param time) const { byte val = 0xff; if (address == 0x7fff) { if (videoEnabled) { val &= 0x7f; } if (!extint) { val &= 0xfe; } } else if (address == 0x7ffe) { if (clock.getTicksTill(time) & 1) { val &= 0xfe; } if (laserdisc && laserdisc->extAck(time)) { val &= 0x7f; } } else if (0x4000 <= address && address < 0x6000) { val = (*rom)[address & 0x1fff]; } return val; } const byte* PioneerLDControl::getReadCacheLine(word start) const { if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) { return nullptr; } else if (0x4000 <= start && start < 0x6000) { return &(*rom)[start & 0x1fff]; } else { return unmappedRead; } } void PioneerLDControl::writeMem(word address, byte value, EmuTime::param time) { if (address == 0x7fff) { // superimpose superimposing = !(value & 1); if (superimposing) { if (extint && !irq.getState()) { irq.set(); } } else if (irq.getState()) { irq.reset(); } updateVideoSource(); // Muting if (!mutel && !(value & 0x80)) { muter = !(ppi->peekIO(2, time) & 0x10); } mutel = !(value & 0x80); if (laserdisc) laserdisc->setMuting(mutel, muter, time); } else if (address == 0x7ffe) { if (laserdisc) laserdisc->extControl(value & 1, time); } } byte* PioneerLDControl::getWriteCacheLine(word start) const { if ((start & CacheLine::HIGH) == (0x7FFE & CacheLine::HIGH)) { return nullptr; } else { return unmappedWrite; } } void PioneerLDControl::videoIn(bool enabled) { if (videoEnabled && !enabled) { // raise an interrupt when external video goes off extint = true; if (superimposing) { irq.set(); } } videoEnabled = enabled; updateVideoSource(); } void PioneerLDControl::updateVideoSource() { auto* videoSource = (videoEnabled && superimposing && laserdisc) ? laserdisc->getRawFrame() : nullptr; vdp->setExternalVideoSource(videoSource); } template void PioneerLDControl::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("clock", clock); ar.serialize("mutel", mutel); ar.serialize("muter", muter); // videoEnabled is restored from LaserdiscPlayer. Set to false // for now so that the irq does not get changed during load if (ar.isLoader()) { videoEnabled = false; } ar.serialize("superimposing", superimposing); ar.serialize("extint", extint); ar.serialize("irq", irq); if (laserdisc) ar.serialize("laserdisc", *laserdisc); if (ar.isLoader()) { updateVideoSource(); if (laserdisc) { laserdisc->setMuting(mutel, muter, getCurrentTime()); } } } REGISTER_MSXDEVICE(PioneerLDControl, "PBASIC"); INSTANTIATE_SERIALIZE_METHODS(PioneerLDControl); } // namespace openmsx openmsx-0.10.0/src/laserdisc/yuv2rgb.hh0000644000175000017500000000036612262345041020532 0ustar manuelmanuel00000000000000#ifndef YUV2RGB_HH #define YUV2RGB_HH #include namespace openmsx { class RawFrame; namespace yuv2rgb { void convert(const th_ycbcr_buffer& input, RawFrame& output); } // namespace yuv2rgb } // namespace openmsx #endif openmsx-0.10.0/src/laserdisc/OggReader.hh0000644000175000017500000000517712262345041020776 0ustar manuelmanuel00000000000000#ifndef OGGREADER_HH #define OGGREADER_HH #include "openmsx.hh" #include "noncopyable.hh" #include #include #include #include #include #include #include #include namespace openmsx { class CliComm; class RawFrame; class File; class Filename; struct AudioFragment { static const size_t UNKNOWN_POS = size_t(-1); static const unsigned MAX_SAMPLES = 2048; size_t position; unsigned length; float pcm[2][MAX_SAMPLES]; }; struct Frame { Frame(const th_ycbcr_buffer& yuv); ~Frame(); th_ycbcr_buffer buffer; size_t no; int length; }; class OggReader : private noncopyable { public: OggReader(const Filename& filename, CliComm& cli); ~OggReader(); bool seek(size_t frame, size_t sample); unsigned getSampleRate() const { return vi.rate; } void getFrameNo(RawFrame& frame, size_t frameno); const AudioFragment* getAudio(size_t sample); size_t getFrames() const; int getFrameRate() const { return frameRate; } // metadata bool stopFrame(size_t frame) const; size_t chapter(int chapterNo) const; private: void cleanup(); void readTheora(ogg_packet* packet); void theoraHeaderPage(ogg_page* page, th_info& ti, th_comment& tc, th_setup_info*& tsi); void readMetadata(th_comment& tc); void readVorbis(ogg_packet* packet); void vorbisHeaderPage(ogg_page* page); bool nextPage(ogg_page* page); bool nextPacket(); void recycleAudio(std::unique_ptr audio); void vorbisFoundPosition(); size_t frameNo(ogg_packet* packet); size_t findOffset(size_t frame, size_t sample); size_t bisection(size_t frame, size_t sample, size_t maxOffset, size_t maxSamples, size_t maxFrames); CliComm& cli; const std::unique_ptr file; enum State { PLAYING, FIND_LAST, FIND_FIRST, FIND_KEYFRAME } state; // ogg state ogg_sync_state sync; ogg_stream_state vorbisStream, theoraStream; int audioSerial; int videoSerial; int skeletonSerial; size_t fileOffset; size_t fileSize; // video th_dec_ctx* theora; int frameRate; size_t keyFrame; size_t currentFrame; int granuleShift; size_t totalFrames; typedef std::deque> Frames; Frames frameList; Frames recycleFrameList; // audio int audioHeaders; vorbis_info vi; vorbis_comment vc; vorbis_dsp_state vd; vorbis_block vb; size_t currentSample; size_t vorbisPos; typedef std::list> AudioFragments; AudioFragments audioList; AudioFragments recycleAudioList; // Metadata std::vector stopFrames; std::vector> chapters; }; } // namespace openmsx #endif openmsx-0.10.0/src/laserdisc/LaserdiscPlayerCLI.hh0000644000175000017500000000123012262345041022537 0ustar manuelmanuel00000000000000#ifndef LASERDISCPLAYERCLI_HH #define LASERDISCPLAYERCLI_HH #include "CLIOption.hh" namespace openmsx { class CommandLineParser; class LaserdiscPlayerCLI : public CLIOption, public CLIFileType { public: explicit LaserdiscPlayerCLI(CommandLineParser& commandLineParser); virtual void parseOption(const std::string& option, std::deque& cmdLine); virtual string_ref optionHelp() const; virtual void parseFileType(const std::string& filename, std::deque& cmdLine); virtual string_ref fileTypeHelp() const; private: CommandLineParser& parser; }; } // namespace openmsx #endif openmsx-0.10.0/src/laserdisc/yuv2rgb.cc0000644000175000017500000003416012262345041020517 0ustar manuelmanuel00000000000000#include "yuv2rgb.hh" #include "RawFrame.hh" #include "Math.hh" #include #include #include #ifdef __SSE2__ #include #endif namespace openmsx { namespace yuv2rgb { #ifdef __SSE2__ /* * This implementation of yuv420 to rgb is based upon the corresponding routine * from Mono. See this blog entry: * http://blog.sublimeintervention.com/archive/2008/Mar-21.html * Source code: * http://anonsvn.mono-project.com/viewvc/trunk/moon/src/yuv-converter.cpp?revision=136072 * This code is GPL2 (only) * * Copyright 2008 Novell, Inc. (http://www.novell.com) * * There are other implementations: * - ffmpeg * - mythtv * - pcsx2 * I have not done a comparison of these implementations. */ /* R = 1.164 * (Y - 16) + 1.596 * (V - 128) * G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128) * B = 1.164 * (Y - 16) + 2.018 * (U - 128) * OR * R = 1.164 * Y + 1.596 * V - 222.921 * G = 1.164 * Y - 0.813 * V - 0.391 * U + 135.576 * B = 1.164 * Y + 2.018 * U - 276.836 */ static inline void yuv2rgb_sse2( const uint8_t* u_ , const uint8_t* v_, const uint8_t* y0_, const uint8_t* y1_, uint32_t* out0_, uint32_t* out1_) { // This routine calculates 32x2 RGBA pixels. Each output pixel uses a // unique corresponding input Y value, but a group of 2x2 ouput pixels // shares the same U and V input value. auto* u = reinterpret_cast(u_); auto* v = reinterpret_cast(v_); auto* y0 = reinterpret_cast(y0_); auto* y1 = reinterpret_cast(y1_); auto* out0 = reinterpret_cast< __m128i*>(out0_); auto* out1 = reinterpret_cast< __m128i*>(out1_); // constants const __m128i ZERO = _mm_setzero_si128(); const __m128i ALPHA = _mm_set1_epi16( -1); // 0xFFFF const __m128i RED_V = _mm_set1_epi16( 102); // 102/64 = 1.59 const __m128i GREEN_U = _mm_set1_epi16( -25); // -25/64 = -0.39 const __m128i GREEN_V = _mm_set1_epi16( -52); // -52/64 = -0.81 const __m128i BLUE_U = _mm_set1_epi16( 129); // 129/64 = 2.02 const __m128i COEF_Y = _mm_set1_epi16( 74); // 74/64 = 1.16 const __m128i CNST_R = _mm_set1_epi16( -223); // -222.921 const __m128i CNST_G = _mm_set1_epi16( 136); // 135.576 const __m128i CNST_B = _mm_set1_epi16( -277); // -276.836 const __m128i Y_MASK = _mm_set1_epi16(0x00FF); // left __m128i u0f = _mm_load_si128(u); __m128i v0f = _mm_load_si128(v); __m128i u07 = _mm_unpacklo_epi8(u0f, ZERO); __m128i v07 = _mm_unpacklo_epi8(v0f, ZERO); __m128i mr07 = _mm_srai_epi16(_mm_mullo_epi16(v07, RED_V), 6); __m128i sg07 = _mm_mullo_epi16(v07, GREEN_V); __m128i tg07 = _mm_mullo_epi16(u07, GREEN_U); __m128i mg07 = _mm_srai_epi16(_mm_adds_epi16(sg07, tg07), 6); __m128i mb07 = _mm_srli_epi16(_mm_mullo_epi16(u07, BLUE_U), 6); // logical shift __m128i dr07 = _mm_adds_epi16(mr07, CNST_R); __m128i dg07 = _mm_adds_epi16(mg07, CNST_G); __m128i db07 = _mm_adds_epi16(mb07, CNST_B); // block top,left __m128i y00_0f = _mm_load_si128(y0 + 0); __m128i y00_even = _mm_and_si128(y00_0f, Y_MASK); __m128i y00_odd = _mm_srli_epi16(y00_0f, 8); __m128i dy00_even = _mm_srai_epi16(_mm_mullo_epi16(y00_even, COEF_Y), 6); __m128i dy00_odd = _mm_srai_epi16(_mm_mullo_epi16(y00_odd, COEF_Y), 6); __m128i r00_even = _mm_adds_epi16(dr07, dy00_even); __m128i g00_even = _mm_adds_epi16(dg07, dy00_even); __m128i b00_even = _mm_adds_epi16(db07, dy00_even); __m128i r00_odd = _mm_adds_epi16(dr07, dy00_odd); __m128i g00_odd = _mm_adds_epi16(dg07, dy00_odd); __m128i b00_odd = _mm_adds_epi16(db07, dy00_odd); __m128i r00_0f = _mm_unpackhi_epi8(_mm_packus_epi16(r00_even, r00_even), _mm_packus_epi16(r00_odd, r00_odd)); __m128i g00_0f = _mm_unpackhi_epi8(_mm_packus_epi16(g00_even, g00_even), _mm_packus_epi16(g00_odd, g00_odd)); __m128i b00_0f = _mm_unpackhi_epi8(_mm_packus_epi16(b00_even, b00_even), _mm_packus_epi16(b00_odd, b00_odd)); __m128i br00_07 = _mm_unpacklo_epi8(b00_0f, r00_0f); __m128i br00_8f = _mm_unpackhi_epi8(b00_0f, r00_0f); __m128i ga00_07 = _mm_unpacklo_epi8(g00_0f, ALPHA); __m128i ga00_8f = _mm_unpackhi_epi8(g00_0f, ALPHA); __m128i bgra00_03 = _mm_unpacklo_epi8(br00_07, ga00_07); __m128i bgra00_47 = _mm_unpackhi_epi8(br00_07, ga00_07); __m128i bgra00_8b = _mm_unpacklo_epi8(br00_8f, ga00_8f); __m128i bgra00_cf = _mm_unpackhi_epi8(br00_8f, ga00_8f); _mm_store_si128(out0 + 0, bgra00_03); _mm_store_si128(out0 + 1, bgra00_47); _mm_store_si128(out0 + 2, bgra00_8b); _mm_store_si128(out0 + 3, bgra00_cf); // block bottom,left __m128i y10_0f = _mm_load_si128(y1 + 0); __m128i y10_even = _mm_and_si128(y10_0f, Y_MASK); __m128i y10_odd = _mm_srli_epi16(y10_0f, 8); __m128i dy10_even = _mm_srai_epi16(_mm_mullo_epi16(y10_even, COEF_Y), 6); __m128i dy10_odd = _mm_srai_epi16(_mm_mullo_epi16(y10_odd, COEF_Y), 6); __m128i r10_even = _mm_adds_epi16(dr07, dy10_even); __m128i g10_even = _mm_adds_epi16(dg07, dy10_even); __m128i b10_even = _mm_adds_epi16(db07, dy10_even); __m128i r10_odd = _mm_adds_epi16(dr07, dy10_odd); __m128i g10_odd = _mm_adds_epi16(dg07, dy10_odd); __m128i b10_odd = _mm_adds_epi16(db07, dy10_odd); __m128i r10_0f = _mm_unpackhi_epi8(_mm_packus_epi16(r10_even, r10_even), _mm_packus_epi16(r10_odd, r10_odd)); __m128i g10_0f = _mm_unpackhi_epi8(_mm_packus_epi16(g10_even, g10_even), _mm_packus_epi16(g10_odd, g10_odd)); __m128i b10_0f = _mm_unpackhi_epi8(_mm_packus_epi16(b10_even, b10_even), _mm_packus_epi16(b10_odd, b10_odd)); __m128i br10_07 = _mm_unpacklo_epi8(b10_0f, r10_0f); __m128i br10_8f = _mm_unpackhi_epi8(b10_0f, r10_0f); __m128i ga10_07 = _mm_unpacklo_epi8(g10_0f, ALPHA); __m128i ga10_8f = _mm_unpackhi_epi8(g10_0f, ALPHA); __m128i bgra10_03 = _mm_unpacklo_epi8(br10_07, ga10_07); __m128i bgra10_47 = _mm_unpackhi_epi8(br10_07, ga10_07); __m128i bgra10_8b = _mm_unpacklo_epi8(br10_8f, ga10_8f); __m128i bgra10_cf = _mm_unpackhi_epi8(br10_8f, ga10_8f); _mm_store_si128(out1 + 0, bgra10_03); _mm_store_si128(out1 + 1, bgra10_47); _mm_store_si128(out1 + 2, bgra10_8b); _mm_store_si128(out1 + 3, bgra10_cf); // right __m128i u8f = _mm_unpackhi_epi8(u0f, ZERO); __m128i v8f = _mm_unpackhi_epi8(v0f, ZERO); __m128i mr8f = _mm_srai_epi16(_mm_mullo_epi16(v8f, RED_V), 6); __m128i sg8f = _mm_mullo_epi16(v8f, GREEN_V); __m128i tg8f = _mm_mullo_epi16(u8f, GREEN_U); __m128i mg8f = _mm_srai_epi16(_mm_adds_epi16(sg8f, tg8f), 6); __m128i mb8f = _mm_srli_epi16(_mm_mullo_epi16(u8f, BLUE_U), 6); // logical shift __m128i dr8f = _mm_adds_epi16(mr8f, CNST_R); __m128i dg8f = _mm_adds_epi16(mg8f, CNST_G); __m128i db8f = _mm_adds_epi16(mb8f, CNST_B); // block top,right __m128i y01_0f = _mm_load_si128(y0 + 1); __m128i y01_even = _mm_and_si128(y01_0f, Y_MASK); __m128i y01_odd = _mm_srli_epi16(y01_0f, 8); __m128i dy01_even = _mm_srai_epi16(_mm_mullo_epi16(y01_even, COEF_Y), 6); __m128i dy01_odd = _mm_srai_epi16(_mm_mullo_epi16(y01_odd, COEF_Y), 6); __m128i r01_even = _mm_adds_epi16(dr8f, dy01_even); __m128i g01_even = _mm_adds_epi16(dg8f, dy01_even); __m128i b01_even = _mm_adds_epi16(db8f, dy01_even); __m128i r01_odd = _mm_adds_epi16(dr8f, dy01_odd); __m128i g01_odd = _mm_adds_epi16(dg8f, dy01_odd); __m128i b01_odd = _mm_adds_epi16(db8f, dy01_odd); __m128i r01_0f = _mm_unpackhi_epi8(_mm_packus_epi16(r01_even, r01_even), _mm_packus_epi16(r01_odd, r01_odd)); __m128i g01_0f = _mm_unpackhi_epi8(_mm_packus_epi16(g01_even, g01_even), _mm_packus_epi16(g01_odd, g01_odd)); __m128i b01_0f = _mm_unpackhi_epi8(_mm_packus_epi16(b01_even, b01_even), _mm_packus_epi16(b01_odd, b01_odd)); __m128i br01_07 = _mm_unpacklo_epi8(b01_0f, r01_0f); __m128i br01_8f = _mm_unpackhi_epi8(b01_0f, r01_0f); __m128i ga01_07 = _mm_unpacklo_epi8(g01_0f, ALPHA); __m128i ga01_8f = _mm_unpackhi_epi8(g01_0f, ALPHA); __m128i bgra01_03 = _mm_unpacklo_epi8(br01_07, ga01_07); __m128i bgra01_47 = _mm_unpackhi_epi8(br01_07, ga01_07); __m128i bgra01_8b = _mm_unpacklo_epi8(br01_8f, ga01_8f); __m128i bgra01_cf = _mm_unpackhi_epi8(br01_8f, ga01_8f); _mm_store_si128(out0 + 4, bgra01_03); _mm_store_si128(out0 + 5, bgra01_47); _mm_store_si128(out0 + 6, bgra01_8b); _mm_store_si128(out0 + 7, bgra01_cf); // block bottom,right __m128i y11_0f = _mm_load_si128(y1 + 1); __m128i y11_even = _mm_and_si128(y11_0f, Y_MASK); __m128i y11_odd = _mm_srli_epi16(y11_0f, 8); __m128i dy11_even = _mm_srai_epi16(_mm_mullo_epi16(y11_even, COEF_Y), 6); __m128i dy11_odd = _mm_srai_epi16(_mm_mullo_epi16(y11_odd, COEF_Y), 6); __m128i r11_even = _mm_adds_epi16(dr8f, dy11_even); __m128i g11_even = _mm_adds_epi16(dg8f, dy11_even); __m128i b11_even = _mm_adds_epi16(db8f, dy11_even); __m128i r11_odd = _mm_adds_epi16(dr8f, dy11_odd); __m128i g11_odd = _mm_adds_epi16(dg8f, dy11_odd); __m128i b11_odd = _mm_adds_epi16(db8f, dy11_odd); __m128i r11_0f = _mm_unpackhi_epi8(_mm_packus_epi16(r11_even, r11_even), _mm_packus_epi16(r11_odd, r11_odd)); __m128i g11_0f = _mm_unpackhi_epi8(_mm_packus_epi16(g11_even, g11_even), _mm_packus_epi16(g11_odd, g11_odd)); __m128i b11_0f = _mm_unpackhi_epi8(_mm_packus_epi16(b11_even, b11_even), _mm_packus_epi16(b11_odd, b11_odd)); __m128i br11_07 = _mm_unpacklo_epi8(b11_0f, r11_0f); __m128i br11_8f = _mm_unpackhi_epi8(b11_0f, r11_0f); __m128i ga11_07 = _mm_unpacklo_epi8(g11_0f, ALPHA); __m128i ga11_8f = _mm_unpackhi_epi8(g11_0f, ALPHA); __m128i bgra11_03 = _mm_unpacklo_epi8(br11_07, ga11_07); __m128i bgra11_47 = _mm_unpackhi_epi8(br11_07, ga11_07); __m128i bgra11_8b = _mm_unpacklo_epi8(br11_8f, ga11_8f); __m128i bgra11_cf = _mm_unpackhi_epi8(br11_8f, ga11_8f); _mm_store_si128(out1 + 4, bgra11_03); _mm_store_si128(out1 + 5, bgra11_47); _mm_store_si128(out1 + 6, bgra11_8b); _mm_store_si128(out1 + 7, bgra11_cf); } static inline void convertHelperSSE2( const th_ycbcr_buffer& buffer, RawFrame& output) { const int width = buffer[0].width; const int y_stride = buffer[0].stride; const int uv_stride2 = buffer[1].stride / 2; assert((width % 32) == 0); assert((buffer[0].height % 2) == 0); for (int y = 0; y < buffer[0].height; y += 2) { const uint8_t* pY1 = buffer[0].data + y * y_stride; const uint8_t* pY2 = buffer[0].data + (y + 1) * y_stride; const uint8_t* pCb = buffer[1].data + y * uv_stride2; const uint8_t* pCr = buffer[2].data + y * uv_stride2; uint32_t* out0 = output.getLinePtrDirect(y + 0); uint32_t* out1 = output.getLinePtrDirect(y + 1); for (int x = 0; x < width; x += 32) { // convert a block of (32 x 2) pixels yuv2rgb_sse2(pCb, pCr, pY1, pY2, out0, out1); pCb += 16; pCr += 16; pY1 += 32; pY2 += 32; out0 += 32; out1 += 32; } output.setLineWidth(y + 0, width); output.setLineWidth(y + 1, width); } } #endif // __SSE2__ static int coefs_gu[256]; static int coefs_gv[256]; static int coefs_bu[256]; static int coefs_rv[256]; static int coefs_y [256]; static const int PREC = 15; static const int COEF_Y = int(1.164 * (1 << PREC) + 0.5); static const int COEF_RV = int(1.596 * (1 << PREC) + 0.5); static const int COEF_GU = int(0.391 * (1 << PREC) + 0.5); static const int COEF_GV = int(0.813 * (1 << PREC) + 0.5); static const int COEF_BU = int(2.018 * (1 << PREC) + 0.5); static void initTables() { static bool init = false; if (init) return; init = true; for (int i = 0; i < 256; ++i) { coefs_gu[i] = -COEF_GU * (i - 128); coefs_gv[i] = -COEF_GV * (i - 128); coefs_bu[i] = COEF_BU * (i - 128); coefs_rv[i] = COEF_RV * (i - 128); coefs_y[i] = COEF_Y * (i - 16) + (PREC / 2); } } template static inline Pixel calc(const SDL_PixelFormat& format, int y, int ruv, int guv, int buv) { uint8_t r = Math::clipIntToByte((y + ruv) >> PREC); uint8_t g = Math::clipIntToByte((y + guv) >> PREC); uint8_t b = Math::clipIntToByte((y + buv) >> PREC); if (sizeof(Pixel) == 4) { return (r << 16) | (g << 8) | (b << 0); } else { return static_cast(SDL_MapRGB(&format, r, g, b)); } } template static void convertHelper(const th_ycbcr_buffer& buffer, RawFrame& output, const SDL_PixelFormat& format) { assert(buffer[1].width * 2 == buffer[0].width); assert(buffer[1].height * 2 == buffer[0].height); const int width = buffer[0].width; const int y_stride = buffer[0].stride; const int uv_stride2 = buffer[1].stride / 2; for (int y = 0; y < buffer[0].height; y += 2) { const uint8_t* pY = buffer[0].data + y * y_stride; const uint8_t* pCb = buffer[1].data + y * uv_stride2; const uint8_t* pCr = buffer[2].data + y * uv_stride2; Pixel* out0 = output.getLinePtrDirect(y + 0); Pixel* out1 = output.getLinePtrDirect(y + 1); for (int x = 0; x < width; x += 2, pY += 2, ++pCr, ++pCb, out0 += 2, out1 += 2) { int ruv = coefs_rv[*pCr]; int guv = coefs_gu[*pCb] + coefs_gv[*pCr]; int buv = coefs_bu[*pCb]; int Y00 = coefs_y[pY[0]]; out0[0] = calc(format, Y00, ruv, guv, buv); int Y01 = coefs_y[pY[1]]; out0[1] = calc(format, Y01, ruv, guv, buv); int Y10 = coefs_y[pY[y_stride + 0]]; out1[0] = calc(format, Y10, ruv, guv, buv); int Y11 = coefs_y[pY[y_stride + 1]]; out1[1] = calc(format, Y11, ruv, guv, buv); } output.setLineWidth(y + 0, width); output.setLineWidth(y + 1, width); } } void convert(const th_ycbcr_buffer& input, RawFrame& output) { initTables(); const SDL_PixelFormat& format = output.getSDLPixelFormat(); if (format.BytesPerPixel == 4) { #ifdef __SSE2__ convertHelperSSE2(input, output); #else convertHelper(input, output, format); #endif } else { assert(format.BytesPerPixel == 2); convertHelper(input, output, format); } } } // namespace yuv2rgb } // namespace openmsx openmsx-0.10.0/src/laserdisc/LaserdiscPlayerCLI.cc0000644000175000017500000000237512262345041022540 0ustar manuelmanuel00000000000000#include "LaserdiscPlayerCLI.hh" #include "CommandLineParser.hh" #include "GlobalCommandController.hh" #include "MSXException.hh" #include "TclObject.hh" using std::deque; using std::string; namespace openmsx { LaserdiscPlayerCLI::LaserdiscPlayerCLI(CommandLineParser& parser_) : parser(parser_) { parser.registerOption("-laserdisc", *this); parser.registerFileType("ogv", *this); } void LaserdiscPlayerCLI::parseOption(const string& option, deque& cmdLine) { parseFileType(getArgument(option, cmdLine), cmdLine); } string_ref LaserdiscPlayerCLI::optionHelp() const { return "Put laserdisc image specified in argument in " "virtual laserdiscplayer"; } void LaserdiscPlayerCLI::parseFileType(const string& filename, deque& /*cmdLine*/) { if (!parser.getGlobalCommandController().hasCommand("laserdiscplayer")) { throw MSXException("No laserdiscplayer."); } TclObject command(parser.getGlobalCommandController().getInterpreter()); command.addListElement("laserdiscplayer"); command.addListElement("insert"); command.addListElement(filename); command.executeCommand(); } string_ref LaserdiscPlayerCLI::fileTypeHelp() const { return "Laserdisc image, Ogg Vorbis/Theora"; } } // namespace openmsx openmsx-0.10.0/src/laserdisc/OggReader.cc0000644000175000017500000005535112262345041020763 0ustar manuelmanuel00000000000000#include "OggReader.hh" #include "Filename.hh" #include "File.hh" #include "MSXException.hh" #include "yuv2rgb.hh" #include "likely.hh" #include "CliComm.hh" #include "StringOp.hh" #include "MemoryOps.hh" #include "memory.hh" #include "stl.hh" #include "stringsp.hh" // for strncasecmp #include #include // for memcpy, memcmp #include // for atoi // TODO // - Improve error handling // - When an non-ogg file is passed, the entire file is scanned // - Clean up this mess! namespace openmsx { Frame::Frame(const th_ycbcr_buffer& yuv) { unsigned y_size = yuv[0].height * yuv[0].stride; unsigned uv_size = yuv[1].height * yuv[1].stride; buffer[0] = yuv[0]; buffer[0].data = static_cast( MemoryOps::mallocAligned(16, y_size)); buffer[1] = yuv[1]; buffer[1].data = static_cast( MemoryOps::mallocAligned(16, uv_size)); buffer[2] = yuv[2]; buffer[2].data = static_cast( MemoryOps::mallocAligned(16, uv_size)); } Frame::~Frame() { MemoryOps::freeAligned(buffer[0].data); MemoryOps::freeAligned(buffer[1].data); MemoryOps::freeAligned(buffer[2].data); } OggReader::OggReader(const Filename& filename, CliComm& cli_) : cli(cli_) , file(make_unique(filename)) { audioSerial = -1; videoSerial = -1; skeletonSerial = -1; audioHeaders = 3; keyFrame = size_t(-1); currentSample = 0; currentFrame = 1; vorbisPos = 0; th_info ti; th_comment tc; th_setup_info* tsi = nullptr; th_info_init(&ti); th_comment_init(&tc); theora = nullptr; vorbis_info_init(&vi); vorbis_comment_init(&vc); ogg_sync_init(&sync); state = PLAYING; fileOffset = 0; fileSize = file->getSize(); ogg_page page; try { while ((audioHeaders || !theora) && nextPage(&page)) { int serial = ogg_page_serialno(&page); if (serial == audioSerial) { vorbisHeaderPage(&page); continue; } else if (serial == videoSerial) { theoraHeaderPage(&page, ti, tc, tsi); continue; } else if (serial == skeletonSerial) { continue; } if (!ogg_page_bos(&page)) { if (videoSerial == -1) { throw MSXException("No video track found"); } if (audioSerial == -1) { throw MSXException("No audio track found"); } // This should be unreachable, right? continue; } ogg_stream_state stream; ogg_packet packet; ogg_stream_init(&stream, serial); ogg_stream_pagein(&stream, &page); if (ogg_stream_packetout(&stream, &packet) <= 0) { ogg_stream_clear(&stream); throw MSXException("Invalid header"); } if (packet.bytes < 8) { ogg_stream_clear(&stream); throw MSXException("Header to small"); } if (memcmp(packet.packet, "\x01vorbis", 7) == 0) { if (audioSerial != -1) { ogg_stream_clear(&stream); throw MSXException("Duplicate audio stream"); } audioSerial = serial; ogg_stream_init(&vorbisStream, serial); vorbisHeaderPage(&page); } else if (memcmp(packet.packet, "\x80theora", 7) == 0) { if (videoSerial != -1) { ogg_stream_clear(&stream); throw MSXException("Duplicate video stream"); } videoSerial = serial; ogg_stream_init(&theoraStream, serial); theoraHeaderPage(&page, ti, tc, tsi); } else if (memcmp(packet.packet, "fishead", 8) == 0) { skeletonSerial = serial; } else if (memcmp(packet.packet, "BBCD", 4) == 0) { ogg_stream_clear(&stream); throw MSXException("DIRAC not supported"); } else if (memcmp(packet.packet, "\177FLAC", 5) == 0) { ogg_stream_clear(&stream); throw MSXException("FLAC not supported"); } else { ogg_stream_clear(&stream); throw MSXException("Unknown stream in ogg file"); } ogg_stream_clear(&stream); } if (videoSerial == -1) { throw MSXException("No video track found"); } if (audioSerial == -1) { throw MSXException("No audio track found"); } if (vi.channels != 2) { throw MSXException("Audio must be stereo"); } if (ti.frame_width != 640 || ti.frame_height != 480) { throw MSXException("Video must be size 640x480"); } if (ti.fps_numerator == 30000 && ti.fps_denominator == 1001) { frameRate = 30; } else if (ti.fps_numerator == 60000 && ti.fps_denominator == 1001) { frameRate = 60; } else { throw MSXException("Video frame rate must be 59.94Hz or 29.97Hz"); } // FIXME: Support YUV444 before release // It would be much better to use YUV444, however the existing // captures are in YUV420 format. yuv2rgb will have to be // updated too. if (ti.pixel_fmt != TH_PF_420) { throw MSXException("Video must be YUV420"); } } catch (MSXException&) { th_setup_free(tsi); th_info_clear(&ti); th_comment_clear(&tc); cleanup(); throw; } th_setup_free(tsi); th_info_clear(&ti); th_comment_clear(&tc); } void OggReader::cleanup() { if (audioHeaders == 0) { vorbis_dsp_clear(&vd); vorbis_block_clear(&vb); } th_decode_free(theora); vorbis_info_clear(&vi); vorbis_comment_clear(&vc); if (audioSerial != -1) { ogg_stream_clear(&vorbisStream); } if (videoSerial != -1) { ogg_stream_clear(&theoraStream); } ogg_sync_clear(&sync); } OggReader::~OggReader() { cleanup(); } /** Vorbis only records the ogg position (in no. of samples) once per ogg * page. After seeking we have already decoded some audio before we encounter * the exact position we are at. Fixup the positions and discard any unwanted * audio. This function expects vorbisPos to be set correctly. */ void OggReader::vorbisFoundPosition() { auto last = vorbisPos; for (auto it = audioList.rbegin(); it != audioList.rend(); ++it) { last -= (*it)->length; (*it)->position = last; } // last is now the first vorbis audio decoded if (last > currentSample) { cli.printWarning("missing part of audio stream"); } if (vorbisPos > currentSample) { currentSample = vorbisPos; } } void OggReader::vorbisHeaderPage(ogg_page* page) { ogg_stream_pagein(&vorbisStream, page); while (true) { ogg_packet packet; int res = ogg_stream_packetout(&vorbisStream, &packet); if (res < 0) { throw MSXException("error in vorbis stream"); } if (res == 0) break; if (audioHeaders == 0) { // ignore, we'll seek to the beginning again before playing continue; } if (packet.packetno <= 2) { if (vorbis_synthesis_headerin(&vi, &vc, &packet) < 0) { throw MSXException("invalid vorbis header"); } --audioHeaders; } if (packet.packetno == 2) { vorbis_synthesis_init(&vd, &vi) ; vorbis_block_init(&vd, &vb); } } } void OggReader::theoraHeaderPage(ogg_page* page, th_info& ti, th_comment& tc, th_setup_info*& tsi) { ogg_stream_pagein(&theoraStream, page); while (true) { ogg_packet packet; int res = ogg_stream_packetout(&theoraStream, &packet); if (res < 0) { throw MSXException("error in vorbis stream"); } if (res == 0) break; if (theora) { // ignore, we'll seek to the beginning again before playing continue; } if (packet.packetno <= 2) { res = th_decode_headerin(&ti, &tc, &tsi, &packet); if (res <= 0) { throw MSXException("invalid theora header"); } } if (packet.packetno == 2) { theora = th_decode_alloc(&ti, tsi); readMetadata(tc); granuleShift = ti.keyframe_granule_shift; } } } void OggReader::readVorbis(ogg_packet* packet) { // deal with header packets if (unlikely(packet->packetno <= 2)) { return; } if (state == FIND_LAST) { if (packet->granulepos != -1) { if ((currentSample == AudioFragment::UNKNOWN_POS) || (size_t(packet->granulepos) > currentSample)) { currentSample = packet->granulepos; } } return; } else if (state == FIND_FIRST) { if (packet->granulepos != -1 && currentSample == AudioFragment::UNKNOWN_POS) { currentSample = packet->granulepos; } return; } else if (state == FIND_KEYFRAME) { // Not relevant for vorbis return; } // generate pcm if (vorbis_synthesis(&vb, packet) != 0) { return; } vorbis_synthesis_blockin(&vd, &vb); float** pcm; long decoded = vorbis_synthesis_pcmout(&vd, &pcm); long pos = 0; while (pos < decoded) { // Find memory to copy PCM into if (recycleAudioList.empty()) { auto audio = make_unique(); audio->length = 0; recycleAudioList.push_front(std::move(audio)); } auto& audio = recycleAudioList.front(); if (audio->length == 0) { audio->position = vorbisPos; } else { // first element was already partially filled } // Copy PCM unsigned len = std::min(decoded - pos, AudioFragment::MAX_SAMPLES - audio->length); memcpy(audio->pcm[0] + audio->length, pcm[0] + pos, len * sizeof(float)); memcpy(audio->pcm[1] + audio->length, pcm[1] + pos, len * sizeof(float)); audio->length += len; pos += len; // Last packet or found position after seeking? bool last = (decoded == pos && (packet->e_o_s || (vorbisPos == AudioFragment::UNKNOWN_POS && packet->granulepos != -1))); if (vorbisPos != AudioFragment::UNKNOWN_POS) { vorbisPos += len; currentSample += len; } if (audio->length == AudioFragment::MAX_SAMPLES || last) { audioList.push_back(std::move(recycleAudioList.front())); recycleAudioList.pop_front(); } } // The granulepos is the no. of samples since the begining of the // stream. Only once per ogg page is this populated. if (packet->granulepos != -1) { if (vorbisPos == AudioFragment::UNKNOWN_POS) { vorbisPos = packet->granulepos; vorbisFoundPosition(); } else { if (vorbisPos != size_t(packet->granulepos)) { cli.printWarning("vorbis audio out of sync, " "expected " + StringOp::toString(vorbisPos) + ", got " + StringOp::toString(packet->granulepos)); vorbisPos = packet->granulepos; } } } // done with PCM data vorbis_synthesis_read(&vd, decoded); } size_t OggReader::frameNo(ogg_packet* packet) { if (packet->granulepos == -1) { return size_t(-1); } size_t intra = packet->granulepos & ((1 << granuleShift) - 1); size_t key = packet->granulepos >> granuleShift; return key + intra; } void OggReader::readMetadata(th_comment& tc) { char* metadata = nullptr; for (int i = 0; i < tc.comments; ++i) { if (!strncasecmp(tc.user_comments[i], "location=", strlen("location="))) { metadata = tc.user_comments[i] + strlen("location="); break; } } if (!metadata) { return; } // Maybe there is a better way of doing this parsing in C++ char* p = metadata; while (p) { if (strncasecmp(p, "chapter: ", 9) == 0) { int chapter = atoi(p + 9); p = strchr(p, ','); if (!p) break; ++p; size_t frame = atol(p); if (frame) { chapters.push_back(std::make_pair(chapter, frame)); } } else if (strncasecmp(p, "stop: ", 6) == 0) { size_t stopframe = atol(p + 6); if (stopframe) { stopFrames.push_back(stopframe); } } p = strchr(p, '\n'); if (p) ++p; } sort(stopFrames.begin(), stopFrames.end()); sort(chapters .begin(), chapters .end(), LessTupleElement<0>()); } void OggReader::readTheora(ogg_packet* packet) { if (th_packet_isheader(packet)) { return; } size_t frameno = frameNo(packet); // If we're seeking, we're only interested in packets with // frame numbers if ((state != PLAYING) && (frameno == size_t(-1))) { return; } if (state == FIND_LAST) { if ((currentFrame == size_t(-1)) || (currentFrame < frameno)) { currentFrame = frameno; } return; } else if (state == FIND_FIRST) { if ((currentFrame == size_t(-1)) || (currentFrame > frameno)) { currentFrame = frameno; } return; } else if (state == FIND_KEYFRAME) { if (frameno < currentFrame) { keyFrame = packet->granulepos >> granuleShift; } else if (currentFrame == frameno) { keyFrame = packet->granulepos >> granuleShift; currentSample = 1; } else if (frameno > currentFrame) { currentSample = 1; } return; } if ((keyFrame != size_t(-1)) && (frameno != size_t(-1)) && (frameno < keyFrame)) { // We're reading before the keyframe, discard return; } if (packet->bytes == 0 && frameList.empty()) { // No use passing empty packets (which represent dup frame) // before we've read any frame. return; } keyFrame = size_t(-1); int rc = th_decode_packetin(theora, packet, nullptr); switch (rc) { case TH_DUPFRAME: if (frameList.empty()) { cli.printWarning("Theora error: dup frame encountered " "without preceding frame"); } else { frameList.back()->length++; } break; case TH_EIMPL: cli.printWarning("Theora error: not capable of reading this"); break; case TH_EFAULT: cli.printWarning("Theora error: API not used correctly"); break; case TH_EBADPACKET: cli.printWarning("Theora error: bad packet"); break; case 0: break; default: cli.printWarning("Theora error: unknown error " + StringOp::toString(rc)); break; } if (rc) { return; } th_ycbcr_buffer yuv; if (th_decode_ycbcr_out(theora, yuv) != 0) { return; } if ((frameno != size_t(-1)) && (frameno < currentFrame)) { return; } currentFrame = frameno + 1; std::unique_ptr frame; if (recycleFrameList.empty()) { frame = make_unique(yuv); } else { frame = std::move(recycleFrameList.back()); recycleFrameList.pop_back(); } int y_size = yuv[0].height * yuv[0].stride; int uv_size = yuv[1].height * yuv[1].stride; memcpy(frame->buffer[0].data, yuv[0].data, y_size); memcpy(frame->buffer[1].data, yuv[1].data, uv_size); memcpy(frame->buffer[2].data, yuv[2].data, uv_size); // At lot of frames have framenumber -1, only some have the correct // frame number. We continue counting from the previous known // postion Frame* last = frameList.empty() ? nullptr : frameList.back().get(); if (last && (last->no != size_t(-1))) { if ((frameno != size_t(-1)) && (frameno != last->no + last->length)) { cli.printWarning("Theora frame sequence wrong"); } else { frameno = last->no + last->length; } } frame->no = frameno; frame->length = 1; // We may read some frames before we encounter one with a proper // frame number. When we do, go back and populate the frame // numbers correctly if (!frameList.empty() && (frameno != size_t(-1)) && (frameList[0]->no == size_t(-1))) { for (auto it = frameList.rbegin(); it != frameList.rend(); ++it) { frameno -= (*it)->length; (*it)->no = frameno; } } frameList.push_back(std::move(frame)); } void OggReader::getFrameNo(RawFrame& rawFrame, size_t frameno) { Frame* frame; while (true) { // If there are no frames or the frames we have read // does not include a proper frame number, just read // more data if (frameList.empty() || (frameList[0]->no == size_t(-1))) { if (!nextPacket()) { return; } continue; } // Remove unneeded frames. Note that at 60Hz the odd and // and even frame are displayed during still, so we can // only throw away the one two frames ago while (frameList.size() >= 3 && frameList[2]->no <= frameno) { recycleFrameList.push_back(std::move(frameList[0])); frameList.pop_front(); } if (!frameList.empty() && frameList[0]->no > frameno) { // we're missing frames! frame = frameList[0].get(); cli.printWarning("Cannot find frame " + StringOp::toString(frameno) + " using " + StringOp::toString(frame->no) + " instead"); break; } if ((frameList.size() >= 2) && ((frameno >= frameList[0]->no) && (frameno < frameList[1]->no))) { frame = frameList[0].get(); break; } if ((frameList.size() >= 3) && ((frameno >= frameList[1]->no) && (frameno < frameList[2]->no))) { frame = frameList[1].get(); break; } // Sanity check, should not happen if (frameList.size() > (2u << granuleShift)) { // We've got more than twice as many frames // as the maximum distance between key frames. cli.printWarning("Cannot find frame " + StringOp::toString(frameno)); return; } // ..add read some new ones if (!nextPacket()) { return; } } yuv2rgb::convert(frame->buffer, rawFrame); } void OggReader::recycleAudio(std::unique_ptr audio) { audio->length = 0; recycleAudioList.push_back(std::move(audio)); } const AudioFragment* OggReader::getAudio(size_t sample) { // Read while position is unknown while (audioList.empty() || audioList.front()->position == AudioFragment::UNKNOWN_POS) { if (!nextPacket()) { return nullptr; } } auto it = audioList.begin(); while (true) { auto& audio = *it; if (audio->position + audio->length + getSampleRate() <= sample) { // Dispose if this, more than 1 second old recycleAudio(std::move(*it)); it = audioList.erase(it); } else if (audio->position + audio->length <= sample) { ++it; } else { if (audio->position <= sample) { return audio.get(); } else { // gone too far? return nullptr; } } // read more if we're at the end of the list if (it == audioList.end()) { size_t size = audioList.size(); while (size == audioList.size()) { if (!nextPacket()) { return nullptr; } } // reset the iterator to not point to the end it = audioList.begin(); } } } bool OggReader::nextPacket() { ogg_packet packet; ogg_page page; while (true) { int ret = ogg_stream_packetout(&vorbisStream, &packet); if (ret == 1) { readVorbis(&packet); return true; } else if (ret == -1) { // recoverable error continue; } ret = ogg_stream_packetout(&theoraStream, &packet); if (ret == 1) { readTheora(&packet); return true; } else if (ret == -1) { // recoverable error continue; } if (!nextPage(&page)) { return false; } int serial = ogg_page_serialno(&page); if (serial == audioSerial) { if (ogg_stream_pagein(&vorbisStream, &page)) { cli.printWarning("Failed to submit vorbis page"); } } else if (serial == videoSerial) { if (ogg_stream_pagein(&theoraStream, &page)) { cli.printWarning("Failed to submit theora page"); } } else if (serial != skeletonSerial) { cli.printWarning("Unexpected stream with serial " + StringOp::toString(serial) + " in ogg file"); } } } bool OggReader::nextPage(ogg_page* page) { static const size_t CHUNK = 4096; int ret; while ((ret = ogg_sync_pageseek(&sync, page)) <= 0) { if (ret < 0) { //throw MSXException("Invalid Ogg file"); } size_t chunk; if (fileSize - fileOffset >= CHUNK) { chunk = CHUNK; } else if (fileOffset < fileSize) { chunk = fileSize - fileOffset; } else { return false; } char* buffer = ogg_sync_buffer(&sync, long(chunk)); file->read(buffer, chunk); fileOffset += chunk; if (ogg_sync_wrote(&sync, long(chunk)) == -1) { cli.printWarning("Internal error: ogg_sync_wrote failed"); } } return true; } size_t OggReader::bisection( size_t frame, size_t sample, size_t maxOffset, size_t maxSamples, size_t maxFrames) { // Defined to be a power-of-two such that the arthmetic can be done faster. // Note that the sample-number is in the range of: 1..(44100*60*60) static const uint64_t SHIFT = 0x20000000ull; uint64_t offsetA = 0, offsetB = maxOffset; uint64_t sampleA = 0, sampleB = maxSamples; uint64_t frameA = 1, frameB = maxFrames; while (true) { uint64_t ratio = (frame - frameA) * SHIFT / (frameB - frameA); if (ratio < 5) { return offsetA; } uint64_t frameOffset = ratio * (offsetB - offsetA) / SHIFT + offsetA; ratio = (sample - sampleA) * SHIFT / (sampleB - sampleA); if (ratio < 5) { return offsetA; } uint64_t sampleOffset = ratio * (offsetB - offsetA) / SHIFT + offsetA; auto offset = std::min(sampleOffset, frameOffset); file->seek(offset); fileOffset = offset; ogg_sync_reset(&sync); currentFrame = size_t(-1); currentSample = AudioFragment::UNKNOWN_POS; state = FIND_FIRST; while (((currentFrame == size_t(-1)) || (currentSample == AudioFragment::UNKNOWN_POS)) && nextPacket()) { // continue reading } state = PLAYING; if (currentSample > sample || currentFrame > frame) { offsetB = offset; sampleB = currentSample; frameB = currentFrame; } else if (currentSample + getSampleRate() < sample && currentFrame + 64 < frame) { offsetA = offset; sampleA = currentSample; frameA = currentFrame; } else { return offset; } } } size_t OggReader::getFrames() const { return totalFrames; } size_t OggReader::findOffset(size_t frame, size_t sample) { static const size_t STEP = 32 * 1024; // first calculate total length in bytes, samples and frames // The file might have changed since we last requested its size, // we assume that only data will be added to it and the ogg streams // are exactly as before fileSize = file->getSize(); auto offset = fileSize - 1; while (offset > 0) { if (offset > STEP) { offset -= STEP; } else { offset = 0; } file->seek(offset); fileOffset = offset; ogg_sync_reset(&sync); currentFrame = size_t(-1); currentSample = AudioFragment::UNKNOWN_POS; state = FIND_LAST; while (nextPacket()) { // continue reading } state = PLAYING; if ((currentFrame != size_t(-1)) && (currentSample != AudioFragment::UNKNOWN_POS)) { break; } } totalFrames = currentFrame; // If we're close to beginning, don't bother searching for it, // just start at the beginning (arbitrary boundary of 1 second). if (sample < getSampleRate() || frame <= 30) { keyFrame = 1; return 0; } auto maxOffset = offset; auto maxSamples = currentSample; auto maxFrames = currentFrame; if ((sample > maxSamples) || (frame > maxFrames)) { sample = maxSamples; frame = maxFrames; } offset = bisection(frame, sample, maxOffset, maxSamples, maxFrames); // Find key frame file->seek(offset); fileOffset = offset; ogg_sync_reset(&sync); currentFrame = frame; currentSample = 0; keyFrame = size_t(-1); state = FIND_KEYFRAME; while (currentSample == 0 && nextPacket()) { // continue reading } state = PLAYING; if ((keyFrame == size_t(-1)) || (frame == keyFrame)) { return offset; } return bisection(keyFrame, sample, maxOffset, maxSamples, maxFrames); } bool OggReader::seek(size_t frame, size_t samples) { // Remove all queued frames recycleFrameList.insert(recycleFrameList.end(), make_move_iterator(frameList.begin()), make_move_iterator(frameList.end())); frameList.clear(); // Remove all queued audio if (!recycleAudioList.empty()) { recycleAudioList.front()->length = 0; } for (auto& a : audioList) { recycleAudio(std::move(a)); } audioList.clear(); fileOffset = findOffset(frame, samples); file->seek(fileOffset); ogg_sync_reset(&sync); vorbisPos = AudioFragment::UNKNOWN_POS; currentFrame = frame; currentSample = samples; vorbis_synthesis_restart(&vd); return true; } bool OggReader::stopFrame(size_t frame) const { return std::binary_search(stopFrames.begin(), stopFrames.end(), frame); } size_t OggReader::chapter(int chapterNo) const { auto it = std::lower_bound(chapters.begin(), chapters.end(), chapterNo, LessTupleElement<0>()); return ((it != chapters.end()) && (it->first == chapterNo)) ? it->second : 0; } } // namespace openmsx openmsx-0.10.0/src/laserdisc/LaserdiscPlayer.cc0000644000175000017500000007430112262345041022206 0ustar manuelmanuel00000000000000#include "LaserdiscPlayer.hh" #include "BooleanSetting.hh" #include "RecordedCommand.hh" #include "CommandException.hh" #include "CommandController.hh" #include "EventDistributor.hh" #include "FileContext.hh" #include "DeviceConfig.hh" #include "HardwareConfig.hh" #include "XMLElement.hh" #include "CassettePort.hh" #include "CliComm.hh" #include "Display.hh" #include "GlobalSettings.hh" #include "Reactor.hh" #include "MSXMotherBoard.hh" #include "PioneerLDControl.hh" #include "OggReader.hh" #include "LDRenderer.hh" #include "ThrottleManager.hh" #include "Math.hh" #include "likely.hh" #include "memory.hh" #include using std::unique_ptr; using std::string; using std::vector; namespace openmsx { // LaserdiscCommand class LaserdiscCommand : public RecordedCommand { public: LaserdiscCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, LaserdiscPlayer& laserdiscPlayer); virtual string execute(const vector& tokens, EmuTime::param time); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: LaserdiscPlayer& laserdiscPlayer; }; LaserdiscCommand::LaserdiscCommand( CommandController& commandController_, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, LaserdiscPlayer& laserdiscPlayer_) : RecordedCommand(commandController_, stateChangeDistributor, scheduler, "laserdiscplayer") , laserdiscPlayer(laserdiscPlayer_) { } string LaserdiscCommand::execute(const vector& tokens, EmuTime::param time) { string result; if (tokens.size() == 1) { Interpreter& interpreter = getInterpreter(); // Returning Tcl lists here, similar to the disk commands in // DiskChanger TclObject tmp(interpreter); tmp.addListElement(getName() + ':'); tmp.addListElement(laserdiscPlayer.getImageName().getResolved()); result += tmp.getString().str(); } else if (tokens.size() == 2 && tokens[1] == "eject") { result += "Ejecting laserdisc."; laserdiscPlayer.eject(time); } else if (tokens.size() == 3 && tokens[1] == "insert") { try { result += "Changing laserdisc."; laserdiscPlayer.setImageName(tokens[2], time); } catch (MSXException& e) { throw CommandException(e.getMessage()); } } else { throw SyntaxError(); } return result; } string LaserdiscCommand::help(const vector& tokens) const { if (tokens.size() >= 2) { if (tokens[1] == "insert") { return "Inserts the specfied laserdisc image into " "the laserdisc player."; } else if (tokens[1] == "eject") { return "Eject the laserdisc."; } } return "laserdiscplayer insert " ": insert a (different) laserdisc image\n" "laserdiscplayer eject " ": eject the laserdisc\n"; } void LaserdiscCommand::tabCompletion(vector& tokens) const { if (tokens.size() == 2) { static const char* const extra[] = { "eject", "insert" }; completeString(tokens, extra); } else if (tokens.size() == 3 && tokens[1] == "insert") { completeFileName(tokens, UserFileContext()); } } // LaserdiscPlayer static XMLElement createXML() { XMLElement xml("laserdiscplayer"); xml.addChild(XMLElement("sound")) .addChild(XMLElement("volume", "30000")); return xml; } LaserdiscPlayer::LaserdiscPlayer( const HardwareConfig& hwConf, PioneerLDControl& ldcontrol_) : ResampledSoundDevice(hwConf.getMotherBoard(), "laserdiscplayer", "Laserdisc Player", 1, true) , Schedulable(hwConf.getMotherBoard().getScheduler()) , motherBoard(hwConf.getMotherBoard()) , ldcontrol(ldcontrol_) , laserdiscCommand(make_unique( motherBoard.getCommandController(), motherBoard.getStateChangeDistributor(), motherBoard.getScheduler(), *this)) , sampleClock(EmuTime::zero) , start(EmuTime::zero) , muteLeft(false) , muteRight(false) , remoteState(REMOTE_IDLE) , remoteLastEdge(EmuTime::zero) , remoteLastBit(false) , remoteProtocol(IR_NONE) , ack(false) , seeking(false) , playerState(PLAYER_STOPPED) , autoRunSetting(make_unique( motherBoard.getCommandController(), "autorunlaserdisc", "automatically try to run Laserdisc", true)) , loadingIndicator(make_unique( motherBoard.getReactor().getGlobalSettings().getThrottleManager())) , sampleReads(0) { motherBoard.getCassettePort().setLaserdiscPlayer(this); Reactor& reactor = motherBoard.getReactor(); reactor.getDisplay().attach(*this); createRenderer(); reactor.getEventDistributor().registerEventListener(OPENMSX_BOOT_EVENT, *this); scheduleDisplayStart(Schedulable::getCurrentTime()); setInputRate(44100); // Initialize with dummy value static XMLElement xml = createXML(); registerSound(DeviceConfig(hwConf, xml)); } LaserdiscPlayer::~LaserdiscPlayer() { unregisterSound(); Reactor& reactor = motherBoard.getReactor(); reactor.getDisplay().detach(*this); reactor.getEventDistributor().unregisterEventListener(OPENMSX_BOOT_EVENT, *this); } void LaserdiscPlayer::scheduleDisplayStart(EmuTime::param time) { Clock<60000, 1001> frameClock(time); // The video is 29.97Hz, however we need to do vblank processing // at the full 59.94Hz setSyncPoint(frameClock + 1, ODD_FRAME); setSyncPoint(frameClock + 2, EVEN_FRAME); } // The protocol used to communicate over the cable for commands to the // laserdisc player is the NEC infrared protocol with minor deviations: // 1) The leader pulse and space is a little shorter. // 2) The remote does not send NEC repeats; full NEC codes are repeated // after 20ms. The main unit does not understand NEC repeats. // 3) No carrier modulation is done over the ext protocol. // // My Laserdisc player is an Pioneer LD-700 which has a remote called // the CU-700. This is much like the CU-CLD106 which is described // here: http://lirc.sourceforge.net/remotes/pioneer/CU-CLD106 // The codes and protocol are exactly the same. void LaserdiscPlayer::extControl(bool bit, EmuTime::param time) { if (remoteLastBit == bit) return; remoteLastBit = bit; // The tolerance here is based on actual measurements of an LD-700 EmuDuration duration = time - remoteLastEdge; remoteLastEdge = time; unsigned usec = duration.getTicksAt(1000000); // microseconds switch (remoteState) { case REMOTE_IDLE: if (bit) { remoteBits = remoteBitNr = 0; remoteState = REMOTE_HEADER_PULSE; } break; case REMOTE_HEADER_PULSE: if (5800 <= usec && usec < 11200) { remoteState = NEC_HEADER_SPACE; } else { remoteState = REMOTE_IDLE; } break; case NEC_HEADER_SPACE: if (3400 <= usec && usec < 6200) { remoteState = NEC_BITS_PULSE; } else { remoteState = REMOTE_IDLE; } break; case NEC_BITS_PULSE: if (usec >= 380 && usec < 1070) { remoteState = NEC_BITS_SPACE; } else { remoteState = REMOTE_IDLE; } break; case NEC_BITS_SPACE: if (1260 <= usec && usec < 4720) { // bit 1 remoteBits |= 1 << remoteBitNr; } else if (usec < 300 || usec >= 1065) { // error remoteState = REMOTE_IDLE; break; } // since it does not matter how long the trailing pulse // is, we process the button here. Note that real hardware // needs the trailing pulse to be at least 200µs if (++remoteBitNr == 32) { byte custom = ( remoteBits >> 0) & 0xff; byte customCompl = (~remoteBits >> 8) & 0xff; byte code = ( remoteBits >> 16) & 0xff; byte codeCompl = (~remoteBits >> 24) & 0xff; if (custom == customCompl && custom == 0xa8 && code == codeCompl) { submitRemote(IR_NEC, code); } remoteState = REMOTE_IDLE; } else { remoteState = NEC_BITS_PULSE; } break; } } void LaserdiscPlayer::submitRemote(RemoteProtocol protocol, unsigned code) { PRT_DEBUG("Laserdisc::submitRemote(" << std::hex << protocol << ", " << code << ')'); // The END command for seeking/waiting acknowledges repeats, // Esh's Aurunmilla needs play as well. if (protocol != remoteProtocol || code != remoteCode || (protocol == IR_NEC && (code == 0x42 || code == 0x17))) { remoteProtocol = protocol; remoteCode = code; remoteVblanksBack = 0; remoteExecuteDelayed = true; } else { PRT_DEBUG("Laserdisc::remote ignored after " << std::dec << remoteVblanksBack << " vblanks"); remoteVblanksBack = 0; remoteExecuteDelayed = false; } } const RawFrame* LaserdiscPlayer::getRawFrame() const { return renderer->getRawFrame(); } void LaserdiscPlayer::setAck(EmuTime::param time, int wait) { PRT_DEBUG("Laserdisc::Lowering ACK for " << std::dec << wait << "ms"); removeSyncPoint(ACK); setSyncPoint(time + EmuDuration::msec(wait), ACK); ack = true; } bool LaserdiscPlayer::extAck(EmuTime::param /*time*/) const { return ack; } void LaserdiscPlayer::remoteButtonNEC(unsigned code, EmuTime::param time) { #ifdef DEBUG string f; switch (code) { case 0x47: f = "C+"; break; // Increase playing speed case 0x46: f = "C-"; break; // Decrease playing speed case 0x43: f = "D+"; break; // Show Frame# & Chapter# OSD case 0x4b: f = "L+"; break; // right case 0x49: f = "L-"; break; // left case 0x4a: f = "L@"; break; // stereo case 0x58: f = "M+"; break; // multi speed forwards case 0x55: f = "M-"; break; // multi speed backwards case 0x17: f = "P+"; break; // play case 0x16: f = "P@"; break; // stop case 0x18: f = "P/"; break; // pause case 0x54: f = "S+"; break; // frame step forward case 0x50: f = "S-"; break; // frame step backwards case 0x45: f = "X+"; break; // clear case 0x41: f = 'F'; break; // seek frame case 0x40: f = 'C'; break; // seek chapter case 0x42: f = "END"; break; // done seek frame/chapter case 0x00: f = '0'; break; case 0x01: f = '1'; break; case 0x02: f = '2'; break; case 0x03: f = '3'; break; case 0x04: f = '4'; break; case 0x05: f = '5'; break; case 0x06: f = '6'; break; case 0x07: f = '7'; break; case 0x08: f = '8'; break; case 0x09: f = '9'; break; case 0x5f: f = "WAIT FRAME"; break; case 0x53: // previous chapter case 0x52: // next chapter default: break; } if (!f.empty()) { PRT_DEBUG("LaserdiscPlayer::remote " << f); } else { PRT_DEBUG("LaserdiscPlayer::remote unknown " << std::hex << code); } #endif // When not playing the following buttons work // 0x17: start playing (ack sent) // 0x16: eject (no ack) // 0x49, 0x4a, 0x4b (ack sent) // if 0x49 is a repeat then no ACK is sent // if 0x49 is followed by 0x4a then ACK is sent if (code == 0x49 || code == 0x4a || code == 0x4b) { updateStream(time); switch (code) { case 0x4b: // L+ (both channels play the left channel) stereoMode = LEFT; break; case 0x49: // L- (both channels play the right channel) stereoMode = RIGHT; break; case 0x4a: // L@ (normal stereo) stereoMode = STEREO; break; } setAck(time, 46); } else if (playerState == PLAYER_STOPPED) { switch (code) { case 0x16: // P@ motherBoard.getMSXCliComm().printWarning( "ejecting laserdisc"); eject(time); break; case 0x17: // P+ play(time); break; } // During playing, playing will be acked if not repeated // within less than 115ms } else { // FIXME: while seeking, only a small subset of buttons work bool nonseekack = true; switch (code) { case 0x5f: seekState = SEEK_WAIT; seekNum = 0; stillOnWaitFrame = false; nonseekack = false; break; case 0x41: seekState = SEEK_FRAME; seekNum = 0; break; case 0x40: seekState = SEEK_CHAPTER; seekNum = 0; nonseekack = video->chapter(0) != 0; break; case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: case 0x09: seekNum = seekNum * 10 + code; break; case 0x42: switch (seekState) { case SEEK_FRAME: seekState = SEEK_NONE; seekFrame(seekNum % 100000, time); nonseekack = false; break; case SEEK_CHAPTER: seekState = SEEK_NONE; seekChapter(seekNum % 100, time); nonseekack = false; break; case SEEK_WAIT: seekState = SEEK_NONE; waitFrame = seekNum % 100000; if (waitFrame >= 101 && waitFrame < 200) { auto frame = video->chapter( int(waitFrame - 100)); if (frame) waitFrame = frame; } PRT_DEBUG("Wait frame set to " << std::dec << waitFrame); break; default: seekState = SEEK_NONE; break; } break; case 0x45: // Clear "X+" if (seekState != SEEK_NONE && seekNum != 0) { seekNum = 0; } else { seekState = SEEK_NONE; seekNum = 0; } waitFrame = 0; break; case 0x18: // P/ pause(time); nonseekack = false; break; case 0x17: // P+ play(time); nonseekack = false; break; case 0x16: // P@ (stop/eject) stop(time); nonseekack = false; break; case 0xff: nonseekack = false; seekState = SEEK_NONE; break; case 0x54: // S+ (frame step forward) if (seekState == SEEK_WAIT) { stillOnWaitFrame = true; } else { stepFrame(true); } break; case 0x50: // S- (frame step backwards) stepFrame(false); break; case 0x55: // M- (multispeed backwards) // Not supported motherBoard.getMSXCliComm().printWarning( "The Laserdisc player received a command to " "play backwards (M-). This is currently not " "supported."); nonseekack = false; break; case 0x58: // M+ (multispeed forwards) playerState = PLAYER_MULTISPEED; setFrameStep(); break; case 0x46: // C- (play slower) if (playingSpeed >= SPEED_STEP1) { playingSpeed--; frameStep = 1; // FIXME: is this correct? } break; case 0x47: // C+ (play faster) if (playingSpeed <= SPEED_X2) { playingSpeed++; frameStep = 1; // FIXME: is this correct? } break; default: motherBoard.getMSXCliComm().printWarning( "The Laserdisc player received an unknown " "command 0x" + StringOp::toHexString(code, 2)); nonseekack = false; break; } if (nonseekack) { // All ACKs for operations which do not // require seeking setAck(time, 46); } } } void LaserdiscPlayer::executeUntil(EmuTime::param time, int userdata) { updateStream(time); switch (userdata) { case ACK: if (seeking && playerState == PLAYER_PLAYING) { sampleClock.advance(time); } if (seeking) { PRT_DEBUG("Laserdisc: seek complete"); } ack = false; seeking = false; PRT_DEBUG("Laserdisc: ACK cleared"); break; case ODD_FRAME: if (!video || video->getFrameRate() != 60) break; case EVEN_FRAME: if ((playerState != PLAYER_STOPPED) && (currentFrame > video->getFrames())) { playerState = PLAYER_STOPPED; } if (RawFrame* rawFrame = renderer->getRawFrame()) { renderer->frameStart(time); if (isVideoOutputAvailable(time)) { auto frame = currentFrame; if (video->getFrameRate() == 60) { frame *= 2; if (userdata == ODD_FRAME) { frame--; } } video->getFrameNo(*rawFrame, frame); if (userdata == EVEN_FRAME) { nextFrame(time); } } else { renderer->drawBlank(0, 128, 196); } renderer->frameEnd(); } // Update throttling loadingIndicator->update(seeking || sampleReads > 500); sampleReads = 0; if (userdata == EVEN_FRAME) { scheduleDisplayStart(time); } } if (userdata == EVEN_FRAME || userdata == ODD_FRAME) { // Processing of the remote control happens at each frame // (even and odd, so at 59.94Hz) if (remoteProtocol == IR_NEC) { if (remoteExecuteDelayed) { remoteButtonNEC(remoteCode, time); } if (++remoteVblanksBack > 6) { remoteProtocol = IR_NONE; } } remoteExecuteDelayed = false; } } void LaserdiscPlayer::setFrameStep() { switch (playingSpeed) { case SPEED_X3: case SPEED_X2: case SPEED_X1: frameStep = 1; break; case SPEED_1IN2: frameStep = 2; break; case SPEED_1IN4: frameStep = 4; break; case SPEED_1IN8: frameStep = 8; break; case SPEED_1IN16: frameStep = 16; break; case SPEED_STEP1: frameStep = 30; break; case SPEED_STEP3: frameStep = 90; break; } } void LaserdiscPlayer::nextFrame(EmuTime::param time) { if (waitFrame && waitFrame == currentFrame) { PRT_DEBUG("LaserdiscPlayer: wait frame " << std::dec << waitFrame << " reached"); // Leave ACK raised until the next command ack = true; waitFrame = 0; if (stillOnWaitFrame) { playingFromSample = getCurrentSample(time); playerState = PLAYER_STILL; stillOnWaitFrame = false; } } if (playerState == PLAYER_MULTISPEED) { if (--frameStep) { return; } switch (playingSpeed) { case SPEED_X3: currentFrame += 3; break; case SPEED_X2: currentFrame += 2; break; default: currentFrame += 1; break; } setFrameStep(); } else if (playerState == PLAYER_PLAYING) { currentFrame++; } // freeze if stop frame if ((playerState == PLAYER_PLAYING || playerState == PLAYER_MULTISPEED) && video->stopFrame(currentFrame)) { PRT_DEBUG("LaserdiscPlayer: stopFrame " << std::dec << currentFrame << " reached"); playingFromSample = getCurrentSample(time); playerState = PLAYER_STILL; } } void LaserdiscPlayer::setImageName(const string& newImage, EmuTime::param time) { stop(time); oggImage = Filename(newImage, UserFileContext()); video = make_unique(oggImage, motherBoard.getMSXCliComm()); unsigned inputRate = video->getSampleRate(); sampleClock.setFreq(inputRate); if (inputRate != getInputRate()) { setInputRate(inputRate); createResampler(); } } const Filename& LaserdiscPlayer::getImageName() const { return oggImage; } int LaserdiscPlayer::signalEvent(const std::shared_ptr& event) { if (event->getType() == OPENMSX_BOOT_EVENT && video) { autoRun(); } return 0; } void LaserdiscPlayer::autoRun() { if (!autoRunSetting->getBoolean()) { return; } string var = "::auto_run_ld_counter"; string command = "if ![info exists " + var + "] { set " + var + " 0 }\n" "incr " + var + "\n" "after time 2 \"if $" + var + "==\\$" + var + " { " "type 1CALLLD\\\\r }\""; try { motherBoard.getCommandController().executeCommand(command); } catch (CommandException& e) { motherBoard.getMSXCliComm().printWarning( "Error executing loading instruction for AutoRun: " + e.getMessage() + "\n Please report a bug."); } } void LaserdiscPlayer::generateChannels(int** buffers, unsigned num) { if (playerState != PLAYER_PLAYING || seeking || (muteLeft && muteRight)) { buffers[0] = nullptr; return; } unsigned pos = 0; size_t currentSample; if (unlikely(!sampleClock.before(start))) { // Before playing of sounds begins EmuDuration duration = sampleClock.getTime() - start; unsigned len = duration.getTicksAt(video->getSampleRate()); if (len >= num) { buffers[0] = nullptr; return; } for (/**/; pos < len; ++pos) { buffers[0][pos * 2 + 0] = 0; buffers[0][pos * 2 + 1] = 0; } currentSample = playingFromSample; } else { currentSample = getCurrentSample(start); } unsigned drift = video->getSampleRate() / 30; if (currentSample > (lastPlayedSample + drift) || (currentSample + drift) < lastPlayedSample) { PRT_DEBUG("Laserdisc audio drift: " << std::dec << lastPlayedSample << ' ' << currentSample); lastPlayedSample = currentSample; } int left = stereoMode == RIGHT ? 1 : 0; int right = stereoMode == LEFT ? 0 : 1; while (pos < num) { const AudioFragment* audio = video->getAudio(lastPlayedSample); if (!audio) { if (pos == 0) { buffers[0] = nullptr; break; } else for (/**/; pos < num; ++pos) { buffers[0][pos * 2 + 0] = 0; buffers[0][pos * 2 + 1] = 0; } } else { auto offset = unsigned(lastPlayedSample - audio->position); unsigned len = std::min(audio->length - offset, num - pos); // maybe muting should be moved out of the loop? for (unsigned i = 0; i < len; ++i, ++pos) { buffers[0][pos * 2 + 0] = muteLeft ? 0 : int(audio->pcm[left][offset + i] * 65536.f); buffers[0][pos * 2 + 1] = muteRight ? 0 : int(audio->pcm[right][offset + i] * 65536.f); } lastPlayedSample += len; } } } bool LaserdiscPlayer::updateBuffer(unsigned length, int* buffer, EmuTime::param time) { bool result = ResampledSoundDevice::updateBuffer(length, buffer, time); start = time; // current end-time is next start-time return result; } void LaserdiscPlayer::setMuting(bool left, bool right, EmuTime::param time) { updateStream(time); PRT_DEBUG("Laserdisc::setMuting L:" << (left ? "on" : "off") << " R:" << (right ? "on" : "off")); muteLeft = left; muteRight = right; } void LaserdiscPlayer::play(EmuTime::param time) { PRT_DEBUG("Laserdisc::Play"); if (video) { updateStream(time); if (seeking) { // Do not ACK PRT_DEBUG("play while seeking"); } else if (playerState == PLAYER_STOPPED) { // Disk needs to spin up, which takes 9.6s on // my Pioneer LD-92000. Also always seek to // beginning (confirmed on real MSX and LD) video->seek(1, 0); lastPlayedSample = 0; playingFromSample = 0; currentFrame = 1; // Note that with "fullspeedwhenloading" this // should be reduced to. setAck(time, 9600); seekState = SEEK_NONE; seeking = true; waitFrame = 0; stereoMode = STEREO; playingSpeed = SPEED_1IN4; } else if (playerState == PLAYER_PLAYING) { // If Play command is issued while the player // is already playing, then if no ACK is sent then // Astron Belt will send LD1100 commands setAck(time, 46); } else if (playerState == PLAYER_MULTISPEED) { // Should be hearing stuff again playingFromSample = (currentFrame - 1ll) * 1001ll * video->getSampleRate() / 30000ll; sampleClock.advance(time); setAck(time, 46); } else { // STILL or PAUSED sampleClock.advance(time); setAck(time, 46); } playerState = PLAYER_PLAYING; } } size_t LaserdiscPlayer::getCurrentSample(EmuTime::param time) { switch(playerState) { case PLAYER_PAUSED: case PLAYER_STILL: return playingFromSample; default: return playingFromSample + sampleClock.getTicksTill(time); } } void LaserdiscPlayer::pause(EmuTime::param time) { if (playerState != PLAYER_STOPPED) { PRT_DEBUG("Laserdisc::Pause"); updateStream(time); if (playerState == PLAYER_PLAYING) { playingFromSample = getCurrentSample(time); } else if (playerState == PLAYER_MULTISPEED) { playingFromSample = (currentFrame - 1ll) * 1001ll * video->getSampleRate() / 30000ll; sampleClock.advance(time); } playerState = PLAYER_PAUSED; setAck(time, 46); } } void LaserdiscPlayer::stop(EmuTime::param time) { if (playerState != PLAYER_STOPPED) { PRT_DEBUG("Laserdisc::Stop"); updateStream(time); playerState = PLAYER_STOPPED; } } void LaserdiscPlayer::eject(EmuTime::param time) { stop(time); video.reset(); } // Step one frame forwards or backwards. The frame will be visible and // we won't be playing afterwards void LaserdiscPlayer::stepFrame(bool forwards) { bool needseek = false; // Note that on real hardware, the screen goes dark momentarily // if you try to step before the first frame or after the last one if (playerState == PLAYER_STILL) { if (forwards) { if (currentFrame < video->getFrames()) { currentFrame++; } } else { if (currentFrame > 1) { currentFrame--; needseek = true; } } } playerState = PLAYER_STILL; int64_t samplePos = (currentFrame - 1ll) * 1001ll * video->getSampleRate() / 30000ll; playingFromSample = samplePos; if (needseek) { if (video->getFrameRate() == 60) video->seek(currentFrame * 2, samplePos); else video->seek(currentFrame, samplePos); } } void LaserdiscPlayer::seekFrame(size_t toframe, EmuTime::param time) { if (playerState != PLAYER_STOPPED) { PRT_DEBUG("Laserdisc::SeekFrame " << std::dec << toframe); if (seeking) { PRT_DEBUG("FIXME: seek command while still seeking"); } if (video) { updateStream(time); if (toframe <= 0) { toframe = 1; } if (toframe > video->getFrames()) { toframe = video->getFrames(); } // Seek time needs to be emulated correctly since // e.g. Astron Belt does not wait for the seek // to complete, it simply assumes a certain // delay. // // This calculation is based on measurements on // a Pioneer LD-92000. auto dist = abs(int64_t(toframe) - int64_t(currentFrame)); int seektime; // time in ms if (dist < 1000) { seektime = dist + 300; } else { seektime = 1800 + dist / 12; } int64_t samplePos = (toframe - 1ll) * 1001ll * video->getSampleRate() / 30000ll; if (video->getFrameRate() == 60) video->seek(toframe * 2, samplePos); else video->seek(toframe, samplePos); playerState = PLAYER_STILL; playingFromSample = samplePos; currentFrame = toframe; // Seeking clears the frame to wait for waitFrame = 0; seeking = true; setAck(time, seektime); } } } void LaserdiscPlayer::seekChapter(int chapter, EmuTime::param time) { if (playerState != PLAYER_STOPPED) { if (video) { auto frameno = video->chapter(chapter); if (!frameno) { return; } seekFrame(frameno, time); } } } short LaserdiscPlayer::readSample(EmuTime::param time) { // Here we should return the value of the sample on the // right audio channel, ignoring muting (this is done in the MSX) // but honouring the stereo mode as this is done in the // Laserdisc player if (playerState == PLAYER_PLAYING && !seeking) { auto sample = getCurrentSample(time); if (const AudioFragment* audio = video->getAudio(sample)) { ++sampleReads; int channel = stereoMode == LEFT ? 0 : 1; return int(audio->pcm[channel][sample - audio->position] * 32767.f); } } return 0; } bool LaserdiscPlayer::isVideoOutputAvailable(EmuTime::param time) { updateStream(time); bool videoOut; switch (playerState) { case PLAYER_PLAYING: case PLAYER_MULTISPEED: case PLAYER_STILL: videoOut = !seeking; break; default: videoOut = false; break; } ldcontrol.videoIn(videoOut); return videoOut; } void LaserdiscPlayer::preVideoSystemChange() { renderer.reset(); } void LaserdiscPlayer::postVideoSystemChange() { createRenderer(); } void LaserdiscPlayer::createRenderer() { Display& display = getMotherBoard().getReactor().getDisplay(); renderer = RendererFactory::createLDRenderer(*this, display); } static enum_string RemoteStateInfo[] = { { "IDLE", LaserdiscPlayer::REMOTE_IDLE }, { "HEADER_PULSE", LaserdiscPlayer::REMOTE_HEADER_PULSE }, { "NEC_HEADER_SPACE", LaserdiscPlayer::NEC_HEADER_SPACE }, { "NEC_BITS_PULSE", LaserdiscPlayer::NEC_BITS_PULSE }, { "NEC_BITS_SPACE", LaserdiscPlayer::NEC_BITS_SPACE }, }; SERIALIZE_ENUM(LaserdiscPlayer::RemoteState, RemoteStateInfo); static enum_string PlayerStateInfo[] = { { "STOPPED", LaserdiscPlayer::PLAYER_STOPPED }, { "PLAYING", LaserdiscPlayer::PLAYER_PLAYING }, { "MULTISPEED", LaserdiscPlayer::PLAYER_MULTISPEED }, { "PAUSED", LaserdiscPlayer::PLAYER_PAUSED }, { "STILL", LaserdiscPlayer::PLAYER_STILL } }; SERIALIZE_ENUM(LaserdiscPlayer::PlayerState, PlayerStateInfo); static enum_string SeekStateInfo[] = { { "NONE", LaserdiscPlayer::SEEK_NONE }, { "CHAPTER", LaserdiscPlayer::SEEK_CHAPTER }, { "FRAME", LaserdiscPlayer::SEEK_FRAME }, { "WAIT", LaserdiscPlayer::SEEK_WAIT } }; SERIALIZE_ENUM(LaserdiscPlayer::SeekState, SeekStateInfo); static enum_string StereoModeInfo[] = { { "LEFT", LaserdiscPlayer::LEFT }, { "RIGHT", LaserdiscPlayer::RIGHT }, { "STEREO", LaserdiscPlayer::STEREO } }; SERIALIZE_ENUM(LaserdiscPlayer::StereoMode, StereoModeInfo); static enum_string RemoteProtocolInfo[] = { { "NONE", LaserdiscPlayer::IR_NONE }, { "NEC", LaserdiscPlayer::IR_NEC }, }; SERIALIZE_ENUM(LaserdiscPlayer::RemoteProtocol, RemoteProtocolInfo); // version 1: initial version // version 2: added 'stillOnWaitFrame' // version 3: reversed bit order of 'remoteBits' and 'remoteCode' template void LaserdiscPlayer::serialize(Archive& ar, unsigned version) { // Serialize remote control ar.serialize("RemoteState", remoteState); if (remoteState != REMOTE_IDLE) { ar.serialize("RemoteBitNr", remoteBitNr); ar.serialize("RemoteBits", remoteBits); if (ar.versionBelow(version, 3)) { assert(ar.isLoader()); remoteBits = Math::reverseNBits(remoteBits, remoteBitNr); } } ar.serialize("RemoteLastBit", remoteLastBit); ar.serialize("RemoteLastEdge", remoteLastEdge); ar.serialize("RemoteProtocol", remoteProtocol); if (remoteProtocol != IR_NONE) { ar.serialize("RemoteCode", remoteCode); if (ar.versionBelow(version, 3)) { assert(ar.isLoader()); remoteCode = Math::reverseByte(remoteCode); } ar.serialize("RemoteExecuteDelayed", remoteExecuteDelayed); ar.serialize("RemoteVblanksBack", remoteVblanksBack); } // Serialize filename ar.serialize("OggImage", oggImage); if (ar.isLoader()) { sampleReads = 0; if (!oggImage.empty()) { setImageName(oggImage.getResolved(), getCurrentTime()); } else { video.reset(); } } ar.serialize("PlayerState", playerState); if (playerState != PLAYER_STOPPED) { // Serialize seek state ar.serialize("SeekState", seekState); if (seekState != SEEK_NONE) { ar.serialize("SeekNum", seekNum); } ar.serialize("seeking", seeking); // Playing state ar.serialize("WaitFrame", waitFrame); // This was not yet implemented in openmsx 0.8.1 and earlier if (ar.versionAtLeast(version, 2)) { ar.serialize("StillOnWaitFrame", stillOnWaitFrame); } ar.serialize("ACK", ack); ar.serialize("PlayingSpeed", playingSpeed); // Frame position ar.serialize("CurrentFrame", currentFrame); if (playerState == PLAYER_MULTISPEED) { ar.serialize("FrameStep", frameStep); } // Audio position ar.serialize("StereoMode", stereoMode); ar.serialize("FromSample", playingFromSample); ar.serialize("SampleClock", sampleClock); if (ar.isLoader()) { // If the samplerate differs, adjust accordingly if (video->getSampleRate() != sampleClock.getFreq()) { uint64_t pos = playingFromSample; pos *= video->getSampleRate(); pos /= sampleClock.getFreq(); playingFromSample = pos; sampleClock.setFreq(video->getSampleRate()); } auto sample = getCurrentSample(getCurrentTime()); if (video->getFrameRate() == 60) video->seek(currentFrame * 2, sample); else video->seek(currentFrame, sample); lastPlayedSample = sample; } } ar.template serializeBase(*this); if (ar.isLoader()) { isVideoOutputAvailable(getCurrentTime()); } } INSTANTIATE_SERIALIZE_METHODS(LaserdiscPlayer); } // namespace openmsx openmsx-0.10.0/src/laserdisc/LaserdiscPlayer.hh0000644000175000017500000001123312262345041022213 0ustar manuelmanuel00000000000000#ifndef LASERDISCPLAYER_HH #define LASERDISCPLAYER_HH #include "ResampledSoundDevice.hh" #include "EmuTime.hh" #include "Schedulable.hh" #include "DynamicClock.hh" #include "Clock.hh" #include "Filename.hh" #include "VideoSystemChangeListener.hh" #include "EventListener.hh" namespace openmsx { class LaserdiscCommand; class PioneerLDControl; class HardwareConfig; class MSXMotherBoard; class OggReader; class BooleanSetting; struct AudioFragment; class LDRenderer; class RawFrame; class LoadingIndicator; class LaserdiscPlayer : public ResampledSoundDevice , public Schedulable , private EventListener , private VideoSystemChangeListener { public: LaserdiscPlayer(const HardwareConfig& hwConf, PioneerLDControl& ldcontrol); ~LaserdiscPlayer(); // Called from CassettePort short readSample(EmuTime::param time); // Called from PioneerLDControl void setMuting(bool left, bool right, EmuTime::param time); bool extAck(EmuTime::param time) const; void extControl(bool bit, EmuTime::param time); const RawFrame* getRawFrame() const; template void serialize(Archive& ar, unsigned version); // video interface MSXMotherBoard& getMotherBoard() { return motherBoard; } enum RemoteState { REMOTE_IDLE, REMOTE_HEADER_PULSE, NEC_HEADER_SPACE, NEC_BITS_PULSE, NEC_BITS_SPACE, }; enum PlayerState { PLAYER_STOPPED, PLAYER_PLAYING, PLAYER_MULTISPEED, PLAYER_PAUSED, PLAYER_STILL }; enum SeekState { SEEK_NONE, SEEK_CHAPTER, SEEK_FRAME, SEEK_WAIT, }; enum StereoMode { LEFT, RIGHT, STEREO }; enum RemoteProtocol { IR_NONE, IR_NEC, }; private: void setImageName(const std::string& newImage, EmuTime::param time); const Filename& getImageName() const; void autoRun(); /** Laserdisc player commands */ void play(EmuTime::param time); void pause(EmuTime::param time); void stop(EmuTime::param time); void eject(EmuTime::param time); void seekFrame(size_t frame, EmuTime::param time); void stepFrame(bool); void seekChapter(int chapter, EmuTime::param time); // Control from MSX /** Is video output being generated? */ void scheduleDisplayStart(EmuTime::param time); bool isVideoOutputAvailable(EmuTime::param time); void remoteButtonNEC(unsigned code, EmuTime::param time); void submitRemote(RemoteProtocol protocol, unsigned code); void setAck(EmuTime::param time, int wait); size_t getCurrentSample(EmuTime::param time); void createRenderer(); // SoundDevice virtual void generateChannels(int** bufs, unsigned num); virtual bool updateBuffer(unsigned length, int* buffer, EmuTime::param time); // Schedulable void executeUntil(EmuTime::param time, int userData); // EventListener virtual int signalEvent(const std::shared_ptr& event); // VideoSystemChangeListener interface: void preVideoSystemChange(); void postVideoSystemChange(); MSXMotherBoard& motherBoard; PioneerLDControl& ldcontrol; const std::unique_ptr laserdiscCommand; std::unique_ptr video; Filename oggImage; std::unique_ptr renderer; void nextFrame(EmuTime::param time); void setFrameStep(); size_t currentFrame; int frameStep; // Audio state DynamicClock sampleClock; EmuTime start; size_t playingFromSample; size_t lastPlayedSample; bool muteLeft, muteRight; StereoMode stereoMode; enum SyncType { EVEN_FRAME, ODD_FRAME, ACK }; // Ext Control RemoteState remoteState; EmuTime remoteLastEdge; unsigned remoteBitNr; unsigned remoteBits; bool remoteLastBit; RemoteProtocol remoteProtocol; unsigned remoteCode; bool remoteExecuteDelayed; // Number of v-blank since code was sent int remoteVblanksBack; /* We need to maintain some state for seeking */ SeekState seekState; /* frame the MSX has requested to wait for */ size_t waitFrame; // pause playing back on reaching wait frame bool stillOnWaitFrame; /* The specific frame or chapter we are seeking to */ int seekNum; // For ack bool ack; // State of the video itself bool seeking; PlayerState playerState; enum PlayingSpeed { SPEED_STEP3 = -5, // Each frame is repeated 90 times SPEED_STEP1 = -4, // Each frame is repeated 30 times SPEED_1IN16 = -3, // Each frame is repeated 16 times SPEED_1IN8 = -2, // Each frame is repeated 8 times SPEED_1IN4 = -1, // Each frame is repeated 4 times SPEED_1IN2 = 0, SPEED_X1 = 1, SPEED_X2 = 2, SPEED_X3 = 3 }; int playingSpeed; // Loading indicator const std::unique_ptr autoRunSetting; const std::unique_ptr loadingIndicator; int sampleReads; friend class LaserdiscCommand; }; SERIALIZE_CLASS_VERSION(LaserdiscPlayer, 3); } // namespace openmsx #endif openmsx-0.10.0/src/laserdisc/PioneerLDControl.hh0000644000175000017500000000266212262345041022315 0ustar manuelmanuel00000000000000#ifndef PIONEERLDCONTROL_HH #define PIONEERLDCONTROL_HH #include "MSXDevice.hh" #include "Clock.hh" #include "IRQHelper.hh" #include namespace openmsx { class Rom; class LaserdiscPlayer; class MSXPPI; class VDP; class PioneerLDControl : public MSXDevice { public: explicit PioneerLDControl(const DeviceConfig& config); virtual ~PioneerLDControl(); virtual void reset(EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte* getWriteCacheLine(word address) const; virtual void init(); void videoIn(bool enabled); template void serialize(Archive& ar, unsigned version); private: void updateVideoSource(); const std::unique_ptr rom; std::unique_ptr laserdisc; MSXPPI* ppi; VDP* vdp; /** * The reference clock for the pulse is 500kHz / 128, which * alternates after each period. We double the frequency * and use modulo 2 for whether it will be high or low. * * See page 88 of the PX-7 Service Manual. */ Clock<2 * 500000, 128> clock; // 2*500kHz / 128 = 7812.5Hz /** * When video output stops, generate an IRQ */ IRQHelper irq; bool extint; bool mutel, muter; bool videoEnabled; bool superimposing; }; } // namespace openmsx #endif openmsx-0.10.0/src/SaveStateCLI.cc0000644000175000017500000000320012262345041017364 0ustar manuelmanuel00000000000000#include "SaveStateCLI.hh" #include "CommandLineParser.hh" #include "GlobalCommandController.hh" #include "MSXException.hh" #include "TclObject.hh" using std::deque; using std::string; namespace openmsx { SaveStateCLI::SaveStateCLI(CommandLineParser& parser_) : parser(parser_) { parser.registerOption("-savestate", *this); parser.registerFileType("oms", *this); } void SaveStateCLI::parseOption(const string& option, deque& cmdLine) { parseFileType(getArgument(option, cmdLine), cmdLine); } string_ref SaveStateCLI::optionHelp() const { return "Load savestate and start emulation from there"; } void SaveStateCLI::parseFileType(const string& filename, deque& /*cmdLine*/) { // TODO: this is basically a C++ version of a part of savestate.tcl. // Can that be improved? GlobalCommandController& controller = parser.getGlobalCommandController(); TclObject command(controller.getInterpreter()); command.addListElement("restore_machine"); command.addListElement(filename); string newId = command.executeCommand(); command = TclObject(controller.getInterpreter()); command.addListElement("machine"); string currentId = command.executeCommand(); if (currentId != "") { command = TclObject(controller.getInterpreter()); command.addListElement("delete_machine"); command.addListElement(currentId); command.executeCommand(); } command = TclObject(controller.getInterpreter()); command.addListElement("activate_machine"); command.addListElement(newId); command.executeCommand(); } string_ref SaveStateCLI::fileTypeHelp() const { return "openMSX savestate"; } } // namespace openmsx openmsx-0.10.0/src/Version.cc0000644000175000017500000000033712262345041016572 0ustar manuelmanuel00000000000000#include "Version.hh" namespace openmsx { #include "Version.ii" std::string Version::full() { return std::string("openMSX ") + VERSION + (RELEASE ? "" : (std::string("-") + REVISION)); } } // namespace openmsx openmsx-0.10.0/src/PatchInterface.hh0000644000175000017500000000070412262345041020035 0ustar manuelmanuel00000000000000#ifndef PATCHINTERFACE_HH #define PATCHINTERFACE_HH #include "Filename.hh" #include "openmsx.hh" #include namespace openmsx { class PatchInterface { public: virtual ~PatchInterface() {} virtual void copyBlock(size_t src, byte* dst, size_t num) const = 0; virtual size_t getSize() const = 0; virtual std::vector getFilenames() const = 0; virtual bool isEmptyPatch() const { return false; } }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXSwitchedDevice.cc0000644000175000017500000000063312262345041020426 0ustar manuelmanuel00000000000000#include "MSXSwitchedDevice.hh" #include "MSXDeviceSwitch.hh" #include "MSXMotherBoard.hh" namespace openmsx { MSXSwitchedDevice::MSXSwitchedDevice(MSXMotherBoard& motherBoard_, byte id_) : motherBoard(motherBoard_), id(id_) { motherBoard.getDeviceSwitch().registerDevice(id, this); } MSXSwitchedDevice::~MSXSwitchedDevice() { motherBoard.getDeviceSwitch().unregisterDevice(id); } } // namespace openmsx openmsx-0.10.0/src/serialize_stl.hh0000644000175000017500000000374012262345041020031 0ustar manuelmanuel00000000000000#ifndef SERIALIZE_STL_HH #define SERIALIZE_STL_HH #include "serialize_core.hh" #include #include #include #include #include #include namespace openmsx { template struct serialize_as_stl_collection : std::true_type { static const int size = -1; // variable size typedef typename T::value_type value_type; // save typedef typename T::const_iterator const_iterator; static const_iterator begin(const T& t) { return t.begin(); } static const_iterator end (const T& t) { return t.end(); } // load static const bool loadInPlace = false; typedef typename std::insert_iterator output_iterator; static void prepare(T& t, int /*n*/) { t.clear(); } static output_iterator output(T& t) { return std::inserter(t, t.begin()); } }; template struct serialize_as_collection> : serialize_as_stl_collection> {}; template struct serialize_as_collection> : serialize_as_stl_collection> {}; template struct serialize_as_collection> : serialize_as_stl_collection> {}; template struct serialize_as_collection> : serialize_as_stl_collection> {}; template struct serialize_as_collection> : serialize_as_stl_collection> { // Override load-part from base class. // Don't load vectors in-place, even though it's technically possible // and slightly more efficient. This is done to keep the correct vector // size at all intermediate steps. This may be important in case an // exception occurs during loading. static const bool loadInPlace = false; typedef typename std::back_insert_iterator> output_iterator; static void prepare(std::vector& v, int n) { v.clear(); v.reserve(n); } static output_iterator output(std::vector& v) { return std::back_inserter(v); } }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXPPI.hh0000644000175000017500000000474212262345041016203 0ustar manuelmanuel00000000000000// This class implements the PPI (8255) // // PPI MSX-I/O Direction MSX-Function // PortA 0xA8 Out Memory primary slot register // PortB 0xA9 In Keyboard column inputs // PortC 0xAA Out Keyboard row select / CAPS / CASo / CASm / SND // Control 0xAB In/Out Mode select for PPI // // Direction indicates the direction normally used on MSX. // Reading from an output port returns the last written byte. // Writing to an input port has no immediate effect. // // PortA combined with upper half of PortC form groupA // PortB lower groupB // GroupA can be in programmed in 3 modes // - basic input/output // - strobed input/output // - bidirectional // GroupB can only use the first two modes. // Only the first mode is used on MSX, only this mode is implemented yet. // // for more detail see // http://w3.qahwah.net/joost/openMSX/8255.pdf #ifndef MSXPPI_HH #define MSXPPI_HH #include "MSXDevice.hh" #include "I8255Interface.hh" #include namespace openmsx { class I8255; class KeyClick; class CassettePortInterface; class RenShaTurbo; class Keyboard; class MSXPPI: public MSXDevice, public I8255Interface { public: explicit MSXPPI(const DeviceConfig& config); virtual ~MSXPPI(); virtual void reset(EmuTime::param time); virtual void powerDown(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: // I8255Interface virtual byte readA(EmuTime::param time); virtual byte readB(EmuTime::param time); virtual nibble readC0(EmuTime::param time); virtual nibble readC1(EmuTime::param time); virtual byte peekA(EmuTime::param time) const; virtual byte peekB(EmuTime::param time) const; virtual nibble peekC0(EmuTime::param time) const; virtual nibble peekC1(EmuTime::param time) const; virtual void writeA(byte value, EmuTime::param time); virtual void writeB(byte value, EmuTime::param time); virtual void writeC0(nibble value, EmuTime::param time); virtual void writeC1(nibble value, EmuTime::param time); CassettePortInterface& cassettePort; RenShaTurbo& renshaTurbo; const std::unique_ptr i8255; const std::unique_ptr click; const std::unique_ptr keyboard; nibble prevBits; nibble selectedRow; }; } // namespace openmsx #endif openmsx-0.10.0/src/PrinterPortSimpl.cc0000644000175000017500000000331412262345041020440 0ustar manuelmanuel00000000000000#include "PrinterPortSimpl.hh" #include "DACSound8U.hh" #include "DeviceConfig.hh" #include "XMLElement.hh" #include "serialize.hh" #include "memory.hh" using std::unique_ptr; namespace openmsx { PrinterPortSimpl::PrinterPortSimpl(const HardwareConfig& hwConf_) : hwConf(hwConf_) { } PrinterPortSimpl::~PrinterPortSimpl() { } bool PrinterPortSimpl::getStatus(EmuTime::param /*time*/) { return true; // TODO check } void PrinterPortSimpl::setStrobe(bool /*strobe*/, EmuTime::param /*time*/) { // ignore strobe // TODO check } void PrinterPortSimpl::writeData(byte data, EmuTime::param time) { dac->writeDAC(data, time); } static XMLElement createXML() { XMLElement xml("simpl"); xml.addChild(XMLElement("sound")) .addChild(XMLElement("volume", "12000")); return xml; } void PrinterPortSimpl::createDAC() { static XMLElement xml = createXML(); dac = make_unique("simpl", getDescription(), DeviceConfig(hwConf, xml)); } void PrinterPortSimpl::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { createDAC(); } void PrinterPortSimpl::unplugHelper(EmuTime::param /*time*/) { dac.reset(); } const std::string& PrinterPortSimpl::getName() const { static const std::string name("simpl"); return name; } string_ref PrinterPortSimpl::getDescription() const { return "Play samples via your printer port."; } template void PrinterPortSimpl::serialize(Archive& ar, unsigned /*version*/) { if (isPluggedIn()) { if (ar.isLoader()) { createDAC(); } ar.serialize("dac", *dac); } } INSTANTIATE_SERIALIZE_METHODS(PrinterPortSimpl); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, PrinterPortSimpl, "PrinterPortSimpl"); } // namespace openmsx openmsx-0.10.0/src/IPSPatch.cc0000644000175000017500000000654712262345041016571 0ustar manuelmanuel00000000000000#include "IPSPatch.hh" #include "File.hh" #include "Filename.hh" #include "MSXException.hh" #include #include #include using std::vector; namespace openmsx { static size_t getStop(const IPSPatch::PatchMap::const_iterator& it) { return it->first + it->second.size(); } IPSPatch::IPSPatch(const Filename& filename_, std::unique_ptr parent_) : filename(filename_) , parent(std::move(parent_)) { File ipsFile(filename); byte buf[5]; ipsFile.read(buf, 5); if (memcmp(buf, "PATCH", 5) != 0) { throw MSXException("Invalid IPS file: " + filename.getOriginal()); } ipsFile.read(buf, 3); while (memcmp(buf, "EOF", 3) != 0) { size_t offset = 0x10000 * buf[0] + 0x100 * buf[1] + buf[2]; ipsFile.read(buf, 2); size_t length = 0x100 * buf[0] + buf[1]; vector v; if (length == 0) { // RLE encoded ipsFile.read(buf, 3); length = 0x100 * buf[0] + buf[1]; v.resize(length, buf[2]); } else { // patch bytes v.resize(length); ipsFile.read(&v.front(), length); } // find overlapping or adjacent patch regions auto b = patchMap.lower_bound(offset); if (b != patchMap.begin()) { --b; if (getStop(b) < offset) ++b; } auto e = patchMap.upper_bound(offset + v.size()); if (b != e) { // remove operlapping regions, merge adjacent regions --e; auto start = std::min(b->first, offset); auto stop = std::max(offset + length, getStop(e)); auto length2 = stop - start; ++e; vector tmp(length2); for (auto it = b; it != e; ++it) { memcpy(&tmp[it->first - start], &it->second[0], it->second.size()); } memcpy(&tmp[offset - start], v.data(), v.size()); patchMap.erase(b, e); patchMap[start] = tmp; } else { // add new region patchMap[offset] = v; } ipsFile.read(buf, 3); } if (patchMap.empty()) { size = parent->getSize(); } else { auto it = --patchMap.end(); size = std::max(parent->getSize(), getStop(it)); } } void IPSPatch::copyBlock(size_t src, byte* dst, size_t num) const { parent->copyBlock(src, dst, num); auto b = patchMap.lower_bound(src); if (b != patchMap.begin()) --b; auto e = patchMap.upper_bound(src + num - 1); for (auto it = b; it != e; ++it) { auto chunkStart = it->first; int chunkSize = int(it->second.size()); // calc chunkOffset, chunkStart int chunkOffset = int(src - chunkStart); if (chunkOffset < 0) { // start after src assert(-chunkOffset < int(num)); // dont start past end chunkOffset = 0; } else if (chunkOffset >= chunkSize) { // chunk completely before src, skip continue; } else { // start before src chunkSize -= chunkOffset; chunkStart += chunkOffset; } // calc chuncksize assert(src <= chunkStart); int overflow = int(chunkStart - src + chunkSize - num); if (overflow > 0) { assert(chunkSize > overflow); chunkSize -= overflow; } // copy assert(chunkOffset < int(it->second.size())); assert((chunkOffset + chunkSize) <= int(it->second.size())); assert(src <= chunkStart); assert((chunkStart + chunkSize) <= (src + num)); memcpy(dst + chunkStart - src, &it->second[chunkOffset], chunkSize); } } size_t IPSPatch::getSize() const { return size; } std::vector IPSPatch::getFilenames() const { auto result = parent->getFilenames(); result.push_back(filename); return result; } } // namespace openmsx openmsx-0.10.0/src/MSXMatsushita.hh0000644000175000017500000000157012262345041017671 0ustar manuelmanuel00000000000000#ifndef MSXMATSUSHITA_HH #define MSXMATSUSHITA_HH #include "MSXDevice.hh" #include "MSXSwitchedDevice.hh" #include namespace openmsx { class FirmwareSwitch; class SRAM; class MSXMatsushita : public MSXDevice, public MSXSwitchedDevice { public: explicit MSXMatsushita(const DeviceConfig& config); virtual ~MSXMatsushita(); // MSXDevice virtual void reset(EmuTime::param time); // MSXSwitchedDevice virtual byte readSwitchedIO(word port, EmuTime::param time); virtual byte peekSwitchedIO(word port, EmuTime::param time) const; virtual void writeSwitchedIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: const std::unique_ptr firmwareSwitch; const std::unique_ptr sram; word address; nibble color1, color2; byte pattern; }; } // namespace openmsx #endif openmsx-0.10.0/src/openmsx.cc0000644000175000017500000000034612262345041016636 0ustar manuelmanuel00000000000000#include "openmsx.hh" #ifdef _WIN32 #include #endif namespace openmsx { #ifdef _WIN32 void DebugPrint(const char* output) { OutputDebugStringA(output); OutputDebugStringA("\n"); } #endif } // namespace openmsx openmsx-0.10.0/src/MSXS1985.hh0000644000175000017500000000165712262345041016306 0ustar manuelmanuel00000000000000/* * This class implements the * backup RAM * bitmap function * of the S1985 MSX-engine * * TODO explanation */ #ifndef S1985_HH #define S1985_HH #include "MSXDevice.hh" #include "MSXSwitchedDevice.hh" #include namespace openmsx { class SRAM; class MSXS1985 : public MSXDevice, public MSXSwitchedDevice { public: explicit MSXS1985(const DeviceConfig& config); virtual ~MSXS1985(); // MSXDevice virtual void reset(EmuTime::param time); // MSXSwitchedDevice virtual byte readSwitchedIO(word port, EmuTime::param time); virtual byte peekSwitchedIO(word port, EmuTime::param time) const; virtual void writeSwitchedIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: std::unique_ptr sram; nibble address; byte color1; byte color2; byte pattern; }; SERIALIZE_CLASS_VERSION(MSXS1985, 2); } // namespace openmsx #endif openmsx-0.10.0/src/utils/0000755000175000017500000000000012262345041015773 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/utils/aligned.hh0000644000175000017500000000247712262345041017731 0ustar manuelmanuel00000000000000#ifndef ALIGNED_HH #define ALIGNED_HH #include "build-info.hh" #include #include #ifdef _MSC_VER #define ALIGNED(EXPRESSION, ALIGNMENT) __declspec (align(ALIGNMENT)) EXPRESSION #else // GCC style #define ALIGNED(EXPRESSION, ALIGNMENT) EXPRESSION __attribute__((__aligned__((ALIGNMENT)))); #endif // Only need to (more strictly) align when SSE is actually enabled. #ifdef __SSE2__ #define SSE_ALIGNED(EXPRESSION) ALIGNED(EXPRESSION, 16) #else #define SSE_ALIGNED(EXPRESSION) EXPRESSION #endif // Unaligned loads and stores. template static inline T unalignedLoad(const void* p) { if (openmsx::OPENMSX_UNALIGNED_MEMORY_ACCESS) { return *reinterpret_cast(p); } else { T t; memcpy(&t, p, sizeof(t)); return t; } } template static inline void unalignedStore(void* p, T t) { if (openmsx::OPENMSX_UNALIGNED_MEMORY_ACCESS) { *reinterpret_cast(p) = t; } else { memcpy(p, &t, sizeof(t)); } } static inline uint32_t unalignedLoad32(const void* p) { return unalignedLoad(p); } static inline uint64_t unalignedLoad64(const void* p) { return unalignedLoad(p); } static inline void unalignedStore32(void* p, uint32_t v) { unalignedStore(p, v); } static inline void unalignedStore64(void* p, uint64_t v) { unalignedStore(p, v); } #endif // ALIGNED_HH openmsx-0.10.0/src/utils/MemoryOps.hh0000644000175000017500000000102212262345041020241 0ustar manuelmanuel00000000000000#ifndef MEMORYOPS_HH #define MEMORYOPS_HH #include #include namespace openmsx { namespace MemoryOps { template struct MemSet { void operator()(Pixel* out, size_t num, Pixel val) const; }; template struct MemSet2 { void operator()(Pixel* out, size_t num, Pixel val0, Pixel val1) const; }; void* mallocAligned(size_t alignment, size_t size); void freeAligned(void* ptr); } // namespace MemoryOps } // namespace openmsx #endif openmsx-0.10.0/src/utils/Observer.hh0000644000175000017500000000055012262345041020103 0ustar manuelmanuel00000000000000#ifndef OBSERVER_HH #define OBSERVER_HH namespace openmsx { /** * Generic Gang-of-Four Observer class, templatized edition. */ template class Observer { public: virtual void update(const T& subject) = 0; virtual void subjectDeleted(const T& /*subject*/) { /*nothing*/ } protected: virtual ~Observer() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/utils/string_ref.hh0000644000175000017500000001126412262345041020462 0ustar manuelmanuel00000000000000#ifndef STRING_REF_HH #define STRING_REF_HH #include #include #include #include #include /** This class implements a subset of the proposal for std::string_ref * (proposed for the next c++ standard (c++1y)). * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3334.html#classstd_1_1basic__string__ref_1ab23a4885309a116e8e67349fe0950290 * * It has an interface that is close to std::string, but it does not own * the memory for the string. Basically it's just a wrapper around: * const char* + length. */ class string_ref { public: typedef size_t size_type; typedef std::ptrdiff_t difference_type; typedef const char* const_iterator; typedef std::reverse_iterator const_reverse_iterator; static const size_type npos = size_type(-1); // construct/copy/assign string_ref() : dat(nullptr), siz(0) {} string_ref(const string_ref& str) : dat(str.dat), siz(str.siz) {} string_ref(const char* str) : dat(str), siz(str ? size_type(strlen(str)) : 0) {} string_ref(const char* str, size_type len) : dat(str), siz(len) { if (!dat) assert(siz == 0); } string_ref(const char* begin, const char* end) : dat(begin), siz(end - begin) { if (!dat) assert(siz == 0); } string_ref(const std::string& str) : dat(str.data()), siz(str.size()) {} string_ref& operator=(const string_ref& rhs) { dat = rhs.data(); siz = rhs.size(); return *this; } // iterators const_iterator begin() const { return dat; } const_iterator end() const { return dat + siz; } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } // capacity size_type size() const { return siz; } bool empty() const { return siz == 0; } //size_type max_size() const; //size_type length() const; // element access char operator[](size_type i) const { assert(i < siz); return dat[i]; } //const char& at(size_type i) const; char front() const { return *dat; } char back() const { return *(dat + siz - 1); } const char* data() const { return dat; } // Outgoing conversion operators //explicit operator std::string() const; // c++11 std::string str() const; // mutators void clear() { siz = 0; } // no need to change 'dat' void remove_prefix(size_type n); void remove_suffix(size_type n); void pop_back() { remove_suffix(1); } void pop_front() { remove_prefix(1); } // string operations with the same semantics as std::string int compare(string_ref x) const; string_ref substr(size_type pos, size_type n = npos) const; //size_type copy(char* buf) const; size_type find(string_ref s) const; size_type find(char c) const; size_type rfind(string_ref s) const; size_type rfind(char c) const; size_type find_first_of(string_ref s) const; size_type find_first_of(char c) const; //size_type find_first_not_of(string_ref s) const; //size_type find_first_not_of(char c) const; size_type find_last_of(string_ref s) const; size_type find_last_of(char c) const; //size_type find_last_not_of(string_ref s) const; //size_type find_last_not_of(char c) const; // new string operations (not part of std::string) bool starts_with(string_ref x) const; bool ends_with(string_ref x) const; private: const char* dat; size_type siz; }; // Comparison operators bool operator==(string_ref x, string_ref y); bool operator< (string_ref x, string_ref y); inline bool operator!=(string_ref x, string_ref y) { return !(x == y); } inline bool operator> (string_ref x, string_ref y) { return (y < x); } inline bool operator<=(string_ref x, string_ref y) { return !(y < x); } inline bool operator>=(string_ref x, string_ref y) { return !(x < y); } // numeric conversions int stoi (string_ref str, string_ref::size_type* idx = nullptr, int base = 0); //long stol (string_ref str, string_ref::size_type* idx = nullptr, int base = 0); unsigned long stoul (string_ref str, string_ref::size_type* idx = nullptr, int base = 0); long long stoll (string_ref str, string_ref::size_type* idx = nullptr, int base = 0); //unsigned long long stoull(string_ref str, string_ref::size_type* idx = nullptr, int base = 0); //float stof (string_ref str, string_ref::size_type* idx = nullptr); //double stod (string_ref str, string_ref::size_type* idx = nullptr); //long double stold (string_ref str, string_ref::size_type* idx = nullptr); // concatenation (this is not part of the std::string_ref proposal) std::string operator+(string_ref x, string_ref y); std::string operator+(char x, string_ref y); std::string operator+(string_ref x, char y); std::ostream& operator<<(std::ostream& os, string_ref str); #endif openmsx-0.10.0/src/utils/tiger.cc0000644000175000017500000010244312262345041017420 0ustar manuelmanuel00000000000000#include "tiger.hh" #include "endian.hh" #include "build-info.hh" #include #include namespace openmsx { std::string TigerHash::toString() const { static const char* const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; std::string result; const uint8_t* src = h8; const uint8_t* end = src + 24; unsigned bit = 0; uint8_t tmp; while (src != end) { if (bit > 3) { tmp = *src & (0xFF >> bit); bit = (bit + 5) % 8; tmp <<= bit; ++src; if (src != end) { tmp |= *src >> (8 - bit); } } else { tmp = (*src >> (3 - bit)) & 0x1F; bit = (bit + 5) % 8; if (bit == 0) ++src; } assert(tmp < 32); result += chars[tmp]; } return result; } // Tiger S boxes static const uint64_t table[4 * 256] = { 0x02AAB17CF7E90C5EULL, 0xAC424B03E243A8ECULL, // 0 0x72CD5BE30DD5FCD3ULL, 0x6D019B93F6F97F3AULL, // 2 0xCD9978FFD21F9193ULL, 0x7573A1C9708029E2ULL, // 4 0xB164326B922A83C3ULL, 0x46883EEE04915870ULL, // 6 0xEAACE3057103ECE6ULL, 0xC54169B808A3535CULL, // 8 0x4CE754918DDEC47CULL, 0x0AA2F4DFDC0DF40CULL, // 10 0x10B76F18A74DBEFAULL, 0xC6CCB6235AD1AB6AULL, // 12 0x13726121572FE2FFULL, 0x1A488C6F199D921EULL, // 14 0x4BC9F9F4DA0007CAULL, 0x26F5E6F6E85241C7ULL, // 16 0x859079DBEA5947B6ULL, 0x4F1885C5C99E8C92ULL, // 18 0xD78E761EA96F864BULL, 0x8E36428C52B5C17DULL, // 20 0x69CF6827373063C1ULL, 0xB607C93D9BB4C56EULL, // 22 0x7D820E760E76B5EAULL, 0x645C9CC6F07FDC42ULL, // 24 0xBF38A078243342E0ULL, 0x5F6B343C9D2E7D04ULL, // 26 0xF2C28AEB600B0EC6ULL, 0x6C0ED85F7254BCACULL, // 28 0x71592281A4DB4FE5ULL, 0x1967FA69CE0FED9FULL, // 30 0xFD5293F8B96545DBULL, 0xC879E9D7F2A7600BULL, // 32 0x860248920193194EULL, 0xA4F9533B2D9CC0B3ULL, // 34 0x9053836C15957613ULL, 0xDB6DCF8AFC357BF1ULL, // 36 0x18BEEA7A7A370F57ULL, 0x037117CA50B99066ULL, // 38 0x6AB30A9774424A35ULL, 0xF4E92F02E325249BULL, // 40 0x7739DB07061CCAE1ULL, 0xD8F3B49CECA42A05ULL, // 42 0xBD56BE3F51382F73ULL, 0x45FAED5843B0BB28ULL, // 44 0x1C813D5C11BF1F83ULL, 0x8AF0E4B6D75FA169ULL, // 46 0x33EE18A487AD9999ULL, 0x3C26E8EAB1C94410ULL, // 48 0xB510102BC0A822F9ULL, 0x141EEF310CE6123BULL, // 50 0xFC65B90059DDB154ULL, 0xE0158640C5E0E607ULL, // 52 0x884E079826C3A3CFULL, 0x930D0D9523C535FDULL, // 54 0x35638D754E9A2B00ULL, 0x4085FCCF40469DD5ULL, // 56 0xC4B17AD28BE23A4CULL, 0xCAB2F0FC6A3E6A2EULL, // 58 0x2860971A6B943FCDULL, 0x3DDE6EE212E30446ULL, // 60 0x6222F32AE01765AEULL, 0x5D550BB5478308FEULL, // 62 0xA9EFA98DA0EDA22AULL, 0xC351A71686C40DA7ULL, // 64 0x1105586D9C867C84ULL, 0xDCFFEE85FDA22853ULL, // 66 0xCCFBD0262C5EEF76ULL, 0xBAF294CB8990D201ULL, // 68 0xE69464F52AFAD975ULL, 0x94B013AFDF133E14ULL, // 70 0x06A7D1A32823C958ULL, 0x6F95FE5130F61119ULL, // 72 0xD92AB34E462C06C0ULL, 0xED7BDE33887C71D2ULL, // 74 0x79746D6E6518393EULL, 0x5BA419385D713329ULL, // 76 0x7C1BA6B948A97564ULL, 0x31987C197BFDAC67ULL, // 78 0xDE6C23C44B053D02ULL, 0x581C49FED002D64DULL, // 80 0xDD474D6338261571ULL, 0xAA4546C3E473D062ULL, // 82 0x928FCE349455F860ULL, 0x48161BBACAAB94D9ULL, // 84 0x63912430770E6F68ULL, 0x6EC8A5E602C6641CULL, // 86 0x87282515337DDD2BULL, 0x2CDA6B42034B701BULL, // 88 0xB03D37C181CB096DULL, 0xE108438266C71C6FULL, // 90 0x2B3180C7EB51B255ULL, 0xDF92B82F96C08BBCULL, // 92 0x5C68C8C0A632F3BAULL, 0x5504CC861C3D0556ULL, // 94 0xABBFA4E55FB26B8FULL, 0x41848B0AB3BACEB4ULL, // 96 0xB334A273AA445D32ULL, 0xBCA696F0A85AD881ULL, // 98 0x24F6EC65B528D56CULL, 0x0CE1512E90F4524AULL, // 100 0x4E9DD79D5506D35AULL, 0x258905FAC6CE9779ULL, // 102 0x2019295B3E109B33ULL, 0xF8A9478B73A054CCULL, // 104 0x2924F2F934417EB0ULL, 0x3993357D536D1BC4ULL, // 106 0x38A81AC21DB6FF8BULL, 0x47C4FBF17D6016BFULL, // 108 0x1E0FAADD7667E3F5ULL, 0x7ABCFF62938BEB96ULL, // 110 0xA78DAD948FC179C9ULL, 0x8F1F98B72911E50DULL, // 112 0x61E48EAE27121A91ULL, 0x4D62F7AD31859808ULL, // 114 0xECEBA345EF5CEAEBULL, 0xF5CEB25EBC9684CEULL, // 116 0xF633E20CB7F76221ULL, 0xA32CDF06AB8293E4ULL, // 118 0x985A202CA5EE2CA4ULL, 0xCF0B8447CC8A8FB1ULL, // 120 0x9F765244979859A3ULL, 0xA8D516B1A1240017ULL, // 122 0x0BD7BA3EBB5DC726ULL, 0xE54BCA55B86ADB39ULL, // 124 0x1D7A3AFD6C478063ULL, 0x519EC608E7669EDDULL, // 126 0x0E5715A2D149AA23ULL, 0x177D4571848FF194ULL, // 128 0xEEB55F3241014C22ULL, 0x0F5E5CA13A6E2EC2ULL, // 130 0x8029927B75F5C361ULL, 0xAD139FABC3D6E436ULL, // 132 0x0D5DF1A94CCF402FULL, 0x3E8BD948BEA5DFC8ULL, // 134 0xA5A0D357BD3FF77EULL, 0xA2D12E251F74F645ULL, // 136 0x66FD9E525E81A082ULL, 0x2E0C90CE7F687A49ULL, // 138 0xC2E8BCBEBA973BC5ULL, 0x000001BCE509745FULL, // 140 0x423777BBE6DAB3D6ULL, 0xD1661C7EAEF06EB5ULL, // 142 0xA1781F354DAACFD8ULL, 0x2D11284A2B16AFFCULL, // 144 0xF1FC4F67FA891D1FULL, 0x73ECC25DCB920ADAULL, // 146 0xAE610C22C2A12651ULL, 0x96E0A810D356B78AULL, // 148 0x5A9A381F2FE7870FULL, 0xD5AD62EDE94E5530ULL, // 150 0xD225E5E8368D1427ULL, 0x65977B70C7AF4631ULL, // 152 0x99F889B2DE39D74FULL, 0x233F30BF54E1D143ULL, // 154 0x9A9675D3D9A63C97ULL, 0x5470554FF334F9A8ULL, // 156 0x166ACB744A4F5688ULL, 0x70C74CAAB2E4AEADULL, // 158 0xF0D091646F294D12ULL, 0x57B82A89684031D1ULL, // 160 0xEFD95A5A61BE0B6BULL, 0x2FBD12E969F2F29AULL, // 162 0x9BD37013FEFF9FE8ULL, 0x3F9B0404D6085A06ULL, // 164 0x4940C1F3166CFE15ULL, 0x09542C4DCDF3DEFBULL, // 166 0xB4C5218385CD5CE3ULL, 0xC935B7DC4462A641ULL, // 168 0x3417F8A68ED3B63FULL, 0xB80959295B215B40ULL, // 170 0xF99CDAEF3B8C8572ULL, 0x018C0614F8FCB95DULL, // 172 0x1B14ACCD1A3ACDF3ULL, 0x84D471F200BB732DULL, // 174 0xC1A3110E95E8DA16ULL, 0x430A7220BF1A82B8ULL, // 176 0xB77E090D39DF210EULL, 0x5EF4BD9F3CD05E9DULL, // 178 0x9D4FF6DA7E57A444ULL, 0xDA1D60E183D4A5F8ULL, // 180 0xB287C38417998E47ULL, 0xFE3EDC121BB31886ULL, // 182 0xC7FE3CCC980CCBEFULL, 0xE46FB590189BFD03ULL, // 184 0x3732FD469A4C57DCULL, 0x7EF700A07CF1AD65ULL, // 186 0x59C64468A31D8859ULL, 0x762FB0B4D45B61F6ULL, // 188 0x155BAED099047718ULL, 0x68755E4C3D50BAA6ULL, // 190 0xE9214E7F22D8B4DFULL, 0x2ADDBF532EAC95F4ULL, // 192 0x32AE3909B4BD0109ULL, 0x834DF537B08E3450ULL, // 194 0xFA209DA84220728DULL, 0x9E691D9B9EFE23F7ULL, // 196 0x0446D288C4AE8D7FULL, 0x7B4CC524E169785BULL, // 198 0x21D87F0135CA1385ULL, 0xCEBB400F137B8AA5ULL, // 200 0x272E2B66580796BEULL, 0x3612264125C2B0DEULL, // 202 0x057702BDAD1EFBB2ULL, 0xD4BABB8EACF84BE9ULL, // 204 0x91583139641BC67BULL, 0x8BDC2DE08036E024ULL, // 206 0x603C8156F49F68EDULL, 0xF7D236F7DBEF5111ULL, // 208 0x9727C4598AD21E80ULL, 0xA08A0896670A5FD7ULL, // 210 0xCB4A8F4309EBA9CBULL, 0x81AF564B0F7036A1ULL, // 212 0xC0B99AA778199ABDULL, 0x959F1EC83FC8E952ULL, // 214 0x8C505077794A81B9ULL, 0x3ACAAF8F056338F0ULL, // 216 0x07B43F50627A6778ULL, 0x4A44AB49F5ECCC77ULL, // 218 0x3BC3D6E4B679EE98ULL, 0x9CC0D4D1CF14108CULL, // 220 0x4406C00B206BC8A0ULL, 0x82A18854C8D72D89ULL, // 222 0x67E366B35C3C432CULL, 0xB923DD61102B37F2ULL, // 224 0x56AB2779D884271DULL, 0xBE83E1B0FF1525AFULL, // 226 0xFB7C65D4217E49A9ULL, 0x6BDBE0E76D48E7D4ULL, // 228 0x08DF828745D9179EULL, 0x22EA6A9ADD53BD34ULL, // 230 0xE36E141C5622200AULL, 0x7F805D1B8CB750EEULL, // 232 0xAFE5C7A59F58E837ULL, 0xE27F996A4FB1C23CULL, // 234 0xD3867DFB0775F0D0ULL, 0xD0E673DE6E88891AULL, // 236 0x123AEB9EAFB86C25ULL, 0x30F1D5D5C145B895ULL, // 238 0xBB434A2DEE7269E7ULL, 0x78CB67ECF931FA38ULL, // 240 0xF33B0372323BBF9CULL, 0x52D66336FB279C74ULL, // 242 0x505F33AC0AFB4EAAULL, 0xE8A5CD99A2CCE187ULL, // 244 0x534974801E2D30BBULL, 0x8D2D5711D5876D90ULL, // 246 0x1F1A412891BC038EULL, 0xD6E2E71D82E56648ULL, // 248 0x74036C3A497732B7ULL, 0x89B67ED96361F5ABULL, // 250 0xFFED95D8F1EA02A2ULL, 0xE72B3BD61464D43DULL, // 252 0xA6300F170BDC4820ULL, 0xEBC18760ED78A77AULL, // 254 0xE6A6BE5A05A12138ULL, 0xB5A122A5B4F87C98ULL, // 256 0x563C6089140B6990ULL, 0x4C46CB2E391F5DD5ULL, // 258 0xD932ADDBC9B79434ULL, 0x08EA70E42015AFF5ULL, // 260 0xD765A6673E478CF1ULL, 0xC4FB757EAB278D99ULL, // 262 0xDF11C6862D6E0692ULL, 0xDDEB84F10D7F3B16ULL, // 264 0x6F2EF604A665EA04ULL, 0x4A8E0F0FF0E0DFB3ULL, // 266 0xA5EDEEF83DBCBA51ULL, 0xFC4F0A2A0EA4371EULL, // 268 0xE83E1DA85CB38429ULL, 0xDC8FF882BA1B1CE2ULL, // 270 0xCD45505E8353E80DULL, 0x18D19A00D4DB0717ULL, // 272 0x34A0CFEDA5F38101ULL, 0x0BE77E518887CAF2ULL, // 274 0x1E341438B3C45136ULL, 0xE05797F49089CCF9ULL, // 276 0xFFD23F9DF2591D14ULL, 0x543DDA228595C5CDULL, // 278 0x661F81FD99052A33ULL, 0x8736E641DB0F7B76ULL, // 280 0x15227725418E5307ULL, 0xE25F7F46162EB2FAULL, // 282 0x48A8B2126C13D9FEULL, 0xAFDC541792E76EEAULL, // 284 0x03D912BFC6D1898FULL, 0x31B1AAFA1B83F51BULL, // 286 0xF1AC2796E42AB7D9ULL, 0x40A3A7D7FCD2EBACULL, // 288 0x1056136D0AFBBCC5ULL, 0x7889E1DD9A6D0C85ULL, // 290 0xD33525782A7974AAULL, 0xA7E25D09078AC09BULL, // 292 0xBD4138B3EAC6EDD0ULL, 0x920ABFBE71EB9E70ULL, // 294 0xA2A5D0F54FC2625CULL, 0xC054E36B0B1290A3ULL, // 296 0xF6DD59FF62FE932BULL, 0x3537354511A8AC7DULL, // 298 0xCA845E9172FADCD4ULL, 0x84F82B60329D20DCULL, // 300 0x79C62CE1CD672F18ULL, 0x8B09A2ADD124642CULL, // 302 0xD0C1E96A19D9E726ULL, 0x5A786A9B4BA9500CULL, // 304 0x0E020336634C43F3ULL, 0xC17B474AEB66D822ULL, // 306 0x6A731AE3EC9BAAC2ULL, 0x8226667AE0840258ULL, // 308 0x67D4567691CAECA5ULL, 0x1D94155C4875ADB5ULL, // 310 0x6D00FD985B813FDFULL, 0x51286EFCB774CD06ULL, // 312 0x5E8834471FA744AFULL, 0xF72CA0AEE761AE2EULL, // 314 0xBE40E4CDAEE8E09AULL, 0xE9970BBB5118F665ULL, // 316 0x726E4BEB33DF1964ULL, 0x703B000729199762ULL, // 318 0x4631D816F5EF30A7ULL, 0xB880B5B51504A6BEULL, // 320 0x641793C37ED84B6CULL, 0x7B21ED77F6E97D96ULL, // 322 0x776306312EF96B73ULL, 0xAE528948E86FF3F4ULL, // 324 0x53DBD7F286A3F8F8ULL, 0x16CADCE74CFC1063ULL, // 326 0x005C19BDFA52C6DDULL, 0x68868F5D64D46AD3ULL, // 328 0x3A9D512CCF1E186AULL, 0x367E62C2385660AEULL, // 330 0xE359E7EA77DCB1D7ULL, 0x526C0773749ABE6EULL, // 332 0x735AE5F9D09F734BULL, 0x493FC7CC8A558BA8ULL, // 334 0xB0B9C1533041AB45ULL, 0x321958BA470A59BDULL, // 336 0x852DB00B5F46C393ULL, 0x91209B2BD336B0E5ULL, // 338 0x6E604F7D659EF19FULL, 0xB99A8AE2782CCB24ULL, // 340 0xCCF52AB6C814C4C7ULL, 0x4727D9AFBE11727BULL, // 342 0x7E950D0C0121B34DULL, 0x756F435670AD471FULL, // 344 0xF5ADD442615A6849ULL, 0x4E87E09980B9957AULL, // 346 0x2ACFA1DF50AEE355ULL, 0xD898263AFD2FD556ULL, // 348 0xC8F4924DD80C8FD6ULL, 0xCF99CA3D754A173AULL, // 350 0xFE477BACAF91BF3CULL, 0xED5371F6D690C12DULL, // 352 0x831A5C285E687094ULL, 0xC5D3C90A3708A0A4ULL, // 354 0x0F7F903717D06580ULL, 0x19F9BB13B8FDF27FULL, // 356 0xB1BD6F1B4D502843ULL, 0x1C761BA38FFF4012ULL, // 358 0x0D1530C4E2E21F3BULL, 0x8943CE69A7372C8AULL, // 360 0xE5184E11FEB5CE66ULL, 0x618BDB80BD736621ULL, // 362 0x7D29BAD68B574D0BULL, 0x81BB613E25E6FE5BULL, // 364 0x071C9C10BC07913FULL, 0xC7BEEB7909AC2D97ULL, // 366 0xC3E58D353BC5D757ULL, 0xEB017892F38F61E8ULL, // 368 0xD4EFFB9C9B1CC21AULL, 0x99727D26F494F7ABULL, // 370 0xA3E063A2956B3E03ULL, 0x9D4A8B9A4AA09C30ULL, // 372 0x3F6AB7D500090FB4ULL, 0x9CC0F2A057268AC0ULL, // 374 0x3DEE9D2DEDBF42D1ULL, 0x330F49C87960A972ULL, // 376 0xC6B2720287421B41ULL, 0x0AC59EC07C00369CULL, // 378 0xEF4EAC49CB353425ULL, 0xF450244EEF0129D8ULL, // 380 0x8ACC46E5CAF4DEB6ULL, 0x2FFEAB63989263F7ULL, // 382 0x8F7CB9FE5D7A4578ULL, 0x5BD8F7644E634635ULL, // 384 0x427A7315BF2DC900ULL, 0x17D0C4AA2125261CULL, // 386 0x3992486C93518E50ULL, 0xB4CBFEE0A2D7D4C3ULL, // 388 0x7C75D6202C5DDD8DULL, 0xDBC295D8E35B6C61ULL, // 390 0x60B369D302032B19ULL, 0xCE42685FDCE44132ULL, // 392 0x06F3DDB9DDF65610ULL, 0x8EA4D21DB5E148F0ULL, // 394 0x20B0FCE62FCD496FULL, 0x2C1B912358B0EE31ULL, // 396 0xB28317B818F5A308ULL, 0xA89C1E189CA6D2CFULL, // 398 0x0C6B18576AAADBC8ULL, 0xB65DEAA91299FAE3ULL, // 400 0xFB2B794B7F1027E7ULL, 0x04E4317F443B5BEBULL, // 402 0x4B852D325939D0A6ULL, 0xD5AE6BEEFB207FFCULL, // 404 0x309682B281C7D374ULL, 0xBAE309A194C3B475ULL, // 406 0x8CC3F97B13B49F05ULL, 0x98A9422FF8293967ULL, // 408 0x244B16B01076FF7CULL, 0xF8BF571C663D67EEULL, // 410 0x1F0D6758EEE30DA1ULL, 0xC9B611D97ADEB9B7ULL, // 412 0xB7AFD5887B6C57A2ULL, 0x6290AE846B984FE1ULL, // 414 0x94DF4CDEACC1A5FDULL, 0x058A5BD1C5483AFFULL, // 416 0x63166CC142BA3C37ULL, 0x8DB8526EB2F76F40ULL, // 418 0xE10880036F0D6D4EULL, 0x9E0523C9971D311DULL, // 420 0x45EC2824CC7CD691ULL, 0x575B8359E62382C9ULL, // 422 0xFA9E400DC4889995ULL, 0xD1823ECB45721568ULL, // 424 0xDAFD983B8206082FULL, 0xAA7D29082386A8CBULL, // 426 0x269FCD4403B87588ULL, 0x1B91F5F728BDD1E0ULL, // 428 0xE4669F39040201F6ULL, 0x7A1D7C218CF04ADEULL, // 430 0x65623C29D79CE5CEULL, 0x2368449096C00BB1ULL, // 432 0xAB9BF1879DA503BAULL, 0xBC23ECB1A458058EULL, // 434 0x9A58DF01BB401ECCULL, 0xA070E868A85F143DULL, // 436 0x4FF188307DF2239EULL, 0x14D565B41A641183ULL, // 438 0xEE13337452701602ULL, 0x950E3DCF3F285E09ULL, // 440 0x59930254B9C80953ULL, 0x3BF299408930DA6DULL, // 442 0xA955943F53691387ULL, 0xA15EDECAA9CB8784ULL, // 444 0x29142127352BE9A0ULL, 0x76F0371FFF4E7AFBULL, // 446 0x0239F450274F2228ULL, 0xBB073AF01D5E868BULL, // 448 0xBFC80571C10E96C1ULL, 0xD267088568222E23ULL, // 450 0x9671A3D48E80B5B0ULL, 0x55B5D38AE193BB81ULL, // 452 0x693AE2D0A18B04B8ULL, 0x5C48B4ECADD5335FULL, // 454 0xFD743B194916A1CAULL, 0x2577018134BE98C4ULL, // 456 0xE77987E83C54A4ADULL, 0x28E11014DA33E1B9ULL, // 458 0x270CC59E226AA213ULL, 0x71495F756D1A5F60ULL, // 460 0x9BE853FB60AFEF77ULL, 0xADC786A7F7443DBFULL, // 462 0x0904456173B29A82ULL, 0x58BC7A66C232BD5EULL, // 464 0xF306558C673AC8B2ULL, 0x41F639C6B6C9772AULL, // 466 0x216DEFE99FDA35DAULL, 0x11640CC71C7BE615ULL, // 468 0x93C43694565C5527ULL, 0xEA038E6246777839ULL, // 470 0xF9ABF3CE5A3E2469ULL, 0x741E768D0FD312D2ULL, // 472 0x0144B883CED652C6ULL, 0xC20B5A5BA33F8552ULL, // 474 0x1AE69633C3435A9DULL, 0x97A28CA4088CFDECULL, // 476 0x8824A43C1E96F420ULL, 0x37612FA66EEEA746ULL, // 478 0x6B4CB165F9CF0E5AULL, 0x43AA1C06A0ABFB4AULL, // 480 0x7F4DC26FF162796BULL, 0x6CBACC8E54ED9B0FULL, // 482 0xA6B7FFEFD2BB253EULL, 0x2E25BC95B0A29D4FULL, // 484 0x86D6A58BDEF1388CULL, 0xDED74AC576B6F054ULL, // 486 0x8030BDBC2B45805DULL, 0x3C81AF70E94D9289ULL, // 488 0x3EFF6DDA9E3100DBULL, 0xB38DC39FDFCC8847ULL, // 490 0x123885528D17B87EULL, 0xF2DA0ED240B1B642ULL, // 492 0x44CEFADCD54BF9A9ULL, 0x1312200E433C7EE6ULL, // 494 0x9FFCC84F3A78C748ULL, 0xF0CD1F72248576BBULL, // 496 0xEC6974053638CFE4ULL, 0x2BA7B67C0CEC4E4CULL, // 498 0xAC2F4DF3E5CE32EDULL, 0xCB33D14326EA4C11ULL, // 500 0xA4E9044CC77E58BCULL, 0x5F513293D934FCEFULL, // 502 0x5DC9645506E55444ULL, 0x50DE418F317DE40AULL, // 504 0x388CB31A69DDE259ULL, 0x2DB4A83455820A86ULL, // 506 0x9010A91E84711AE9ULL, 0x4DF7F0B7B1498371ULL, // 508 0xD62A2EABC0977179ULL, 0x22FAC097AA8D5C0EULL, // 510 0xF49FCC2FF1DAF39BULL, 0x487FD5C66FF29281ULL, // 512 0xE8A30667FCDCA83FULL, 0x2C9B4BE3D2FCCE63ULL, // 514 0xDA3FF74B93FBBBC2ULL, 0x2FA165D2FE70BA66ULL, // 516 0xA103E279970E93D4ULL, 0xBECDEC77B0E45E71ULL, // 518 0xCFB41E723985E497ULL, 0xB70AAA025EF75017ULL, // 520 0xD42309F03840B8E0ULL, 0x8EFC1AD035898579ULL, // 522 0x96C6920BE2B2ABC5ULL, 0x66AF4163375A9172ULL, // 524 0x2174ABDCCA7127FBULL, 0xB33CCEA64A72FF41ULL, // 526 0xF04A4933083066A5ULL, 0x8D970ACDD7289AF5ULL, // 528 0x8F96E8E031C8C25EULL, 0xF3FEC02276875D47ULL, // 530 0xEC7BF310056190DDULL, 0xF5ADB0AEBB0F1491ULL, // 532 0x9B50F8850FD58892ULL, 0x4975488358B74DE8ULL, // 534 0xA3354FF691531C61ULL, 0x0702BBE481D2C6EEULL, // 536 0x89FB24057DEDED98ULL, 0xAC3075138596E902ULL, // 538 0x1D2D3580172772EDULL, 0xEB738FC28E6BC30DULL, // 540 0x5854EF8F63044326ULL, 0x9E5C52325ADD3BBEULL, // 542 0x90AA53CF325C4623ULL, 0xC1D24D51349DD067ULL, // 544 0x2051CFEEA69EA624ULL, 0x13220F0A862E7E4FULL, // 546 0xCE39399404E04864ULL, 0xD9C42CA47086FCB7ULL, // 548 0x685AD2238A03E7CCULL, 0x066484B2AB2FF1DBULL, // 550 0xFE9D5D70EFBF79ECULL, 0x5B13B9DD9C481854ULL, // 552 0x15F0D475ED1509ADULL, 0x0BEBCD060EC79851ULL, // 554 0xD58C6791183AB7F8ULL, 0xD1187C5052F3EEE4ULL, // 556 0xC95D1192E54E82FFULL, 0x86EEA14CB9AC6CA2ULL, // 558 0x3485BEB153677D5DULL, 0xDD191D781F8C492AULL, // 560 0xF60866BAA784EBF9ULL, 0x518F643BA2D08C74ULL, // 562 0x8852E956E1087C22ULL, 0xA768CB8DC410AE8DULL, // 564 0x38047726BFEC8E1AULL, 0xA67738B4CD3B45AAULL, // 566 0xAD16691CEC0DDE19ULL, 0xC6D4319380462E07ULL, // 568 0xC5A5876D0BA61938ULL, 0x16B9FA1FA58FD840ULL, // 570 0x188AB1173CA74F18ULL, 0xABDA2F98C99C021FULL, // 572 0x3E0580AB134AE816ULL, 0x5F3B05B773645ABBULL, // 574 0x2501A2BE5575F2F6ULL, 0x1B2F74004E7E8BA9ULL, // 576 0x1CD7580371E8D953ULL, 0x7F6ED89562764E30ULL, // 578 0xB15926FF596F003DULL, 0x9F65293DA8C5D6B9ULL, // 580 0x6ECEF04DD690F84CULL, 0x4782275FFF33AF88ULL, // 582 0xE41433083F820801ULL, 0xFD0DFE409A1AF9B5ULL, // 584 0x4325A3342CDB396BULL, 0x8AE77E62B301B252ULL, // 586 0xC36F9E9F6655615AULL, 0x85455A2D92D32C09ULL, // 588 0xF2C7DEA949477485ULL, 0x63CFB4C133A39EBAULL, // 590 0x83B040CC6EBC5462ULL, 0x3B9454C8FDB326B0ULL, // 592 0x56F56A9E87FFD78CULL, 0x2DC2940D99F42BC6ULL, // 594 0x98F7DF096B096E2DULL, 0x19A6E01E3AD852BFULL, // 596 0x42A99CCBDBD4B40BULL, 0xA59998AF45E9C559ULL, // 598 0x366295E807D93186ULL, 0x6B48181BFAA1F773ULL, // 600 0x1FEC57E2157A0A1DULL, 0x4667446AF6201AD5ULL, // 602 0xE615EBCACFB0F075ULL, 0xB8F31F4F68290778ULL, // 604 0x22713ED6CE22D11EULL, 0x3057C1A72EC3C93BULL, // 606 0xCB46ACC37C3F1F2FULL, 0xDBB893FD02AAF50EULL, // 608 0x331FD92E600B9FCFULL, 0xA498F96148EA3AD6ULL, // 610 0xA8D8426E8B6A83EAULL, 0xA089B274B7735CDCULL, // 612 0x87F6B3731E524A11ULL, 0x118808E5CBC96749ULL, // 614 0x9906E4C7B19BD394ULL, 0xAFED7F7E9B24A20CULL, // 616 0x6509EADEEB3644A7ULL, 0x6C1EF1D3E8EF0EDEULL, // 618 0xB9C97D43E9798FB4ULL, 0xA2F2D784740C28A3ULL, // 620 0x7B8496476197566FULL, 0x7A5BE3E6B65F069DULL, // 622 0xF96330ED78BE6F10ULL, 0xEEE60DE77A076A15ULL, // 624 0x2B4BEE4AA08B9BD0ULL, 0x6A56A63EC7B8894EULL, // 626 0x02121359BA34FEF4ULL, 0x4CBF99F8283703FCULL, // 628 0x398071350CAF30C8ULL, 0xD0A77A89F017687AULL, // 630 0xF1C1A9EB9E423569ULL, 0x8C7976282DEE8199ULL, // 632 0x5D1737A5DD1F7ABDULL, 0x4F53433C09A9FA80ULL, // 634 0xFA8B0C53DF7CA1D9ULL, 0x3FD9DCBC886CCB77ULL, // 636 0xC040917CA91B4720ULL, 0x7DD00142F9D1DCDFULL, // 638 0x8476FC1D4F387B58ULL, 0x23F8E7C5F3316503ULL, // 640 0x032A2244E7E37339ULL, 0x5C87A5D750F5A74BULL, // 642 0x082B4CC43698992EULL, 0xDF917BECB858F63CULL, // 644 0x3270B8FC5BF86DDAULL, 0x10AE72BB29B5DD76ULL, // 646 0x576AC94E7700362BULL, 0x1AD112DAC61EFB8FULL, // 648 0x691BC30EC5FAA427ULL, 0xFF246311CC327143ULL, // 650 0x3142368E30E53206ULL, 0x71380E31E02CA396ULL, // 652 0x958D5C960AAD76F1ULL, 0xF8D6F430C16DA536ULL, // 654 0xC8FFD13F1BE7E1D2ULL, 0x7578AE66004DDBE1ULL, // 656 0x05833F01067BE646ULL, 0xBB34B5AD3BFE586DULL, // 658 0x095F34C9A12B97F0ULL, 0x247AB64525D60CA8ULL, // 660 0xDCDBC6F3017477D1ULL, 0x4A2E14D4DECAD24DULL, // 662 0xBDB5E6D9BE0A1EEBULL, 0x2A7E70F7794301ABULL, // 664 0xDEF42D8A270540FDULL, 0x01078EC0A34C22C1ULL, // 666 0xE5DE511AF4C16387ULL, 0x7EBB3A52BD9A330AULL, // 668 0x77697857AA7D6435ULL, 0x004E831603AE4C32ULL, // 670 0xE7A21020AD78E312ULL, 0x9D41A70C6AB420F2ULL, // 672 0x28E06C18EA1141E6ULL, 0xD2B28CBD984F6B28ULL, // 674 0x26B75F6C446E9D83ULL, 0xBA47568C4D418D7FULL, // 676 0xD80BADBFE6183D8EULL, 0x0E206D7F5F166044ULL, // 678 0xE258A43911CBCA3EULL, 0x723A1746B21DC0BCULL, // 680 0xC7CAA854F5D7CDD3ULL, 0x7CAC32883D261D9CULL, // 682 0x7690C26423BA942CULL, 0x17E55524478042B8ULL, // 684 0xE0BE477656A2389FULL, 0x4D289B5E67AB2DA0ULL, // 686 0x44862B9C8FBBFD31ULL, 0xB47CC8049D141365ULL, // 688 0x822C1B362B91C793ULL, 0x4EB14655FB13DFD8ULL, // 690 0x1ECBBA0714E2A97BULL, 0x6143459D5CDE5F14ULL, // 692 0x53A8FBF1D5F0AC89ULL, 0x97EA04D81C5E5B00ULL, // 694 0x622181A8D4FDB3F3ULL, 0xE9BCD341572A1208ULL, // 696 0x1411258643CCE58AULL, 0x9144C5FEA4C6E0A4ULL, // 698 0x0D33D06565CF620FULL, 0x54A48D489F219CA1ULL, // 700 0xC43E5EAC6D63C821ULL, 0xA9728B3A72770DAFULL, // 702 0xD7934E7B20DF87EFULL, 0xE35503B61A3E86E5ULL, // 704 0xCAE321FBC819D504ULL, 0x129A50B3AC60BFA6ULL, // 706 0xCD5E68EA7E9FB6C3ULL, 0xB01C90199483B1C7ULL, // 708 0x3DE93CD5C295376CULL, 0xAED52EDF2AB9AD13ULL, // 710 0x2E60F512C0A07884ULL, 0xBC3D86A3E36210C9ULL, // 712 0x35269D9B163951CEULL, 0x0C7D6E2AD0CDB5FAULL, // 714 0x59E86297D87F5733ULL, 0x298EF221898DB0E7ULL, // 716 0x55000029D1A5AA7EULL, 0x8BC08AE1B5061B45ULL, // 718 0xC2C31C2B6C92703AULL, 0x94CC596BAF25EF42ULL, // 720 0x0A1D73DB22540456ULL, 0x04B6A0F9D9C4179AULL, // 722 0xEFFDAFA2AE3D3C60ULL, 0xF7C8075BB49496C4ULL, // 724 0x9CC5C7141D1CD4E3ULL, 0x78BD1638218E5534ULL, // 726 0xB2F11568F850246AULL, 0xEDFABCFA9502BC29ULL, // 728 0x796CE5F2DA23051BULL, 0xAAE128B0DC93537CULL, // 730 0x3A493DA0EE4B29AEULL, 0xB5DF6B2C416895D7ULL, // 732 0xFCABBD25122D7F37ULL, 0x70810B58105DC4B1ULL, // 734 0xE10FDD37F7882A90ULL, 0x524DCAB5518A3F5CULL, // 736 0x3C9E85878451255BULL, 0x4029828119BD34E2ULL, // 738 0x74A05B6F5D3CECCBULL, 0xB610021542E13ECAULL, // 740 0x0FF979D12F59E2ACULL, 0x6037DA27E4F9CC50ULL, // 742 0x5E92975A0DF1847DULL, 0xD66DE190D3E623FEULL, // 744 0x5032D6B87B568048ULL, 0x9A36B7CE8235216EULL, // 746 0x80272A7A24F64B4AULL, 0x93EFED8B8C6916F7ULL, // 748 0x37DDBFF44CCE1555ULL, 0x4B95DB5D4B99BD25ULL, // 750 0x92D3FDA169812FC0ULL, 0xFB1A4A9A90660BB6ULL, // 752 0x730C196946A4B9B2ULL, 0x81E289AA7F49DA68ULL, // 754 0x64669A0F83B1A05FULL, 0x27B3FF7D9644F48BULL, // 756 0xCC6B615C8DB675B3ULL, 0x674F20B9BCEBBE95ULL, // 758 0x6F31238275655982ULL, 0x5AE488713E45CF05ULL, // 760 0xBF619F9954C21157ULL, 0xEABAC46040A8EAE9ULL, // 762 0x454C6FE9F2C0C1CDULL, 0x419CF6496412691CULL, // 764 0xD3DC3BEF265B0F70ULL, 0x6D0E60F5C3578A9EULL, // 766 0x5B0E608526323C55ULL, 0x1A46C1A9FA1B59F5ULL, // 768 0xA9E245A17C4C8FFAULL, 0x65CA5159DB2955D7ULL, // 770 0x05DB0A76CE35AFC2ULL, 0x81EAC77EA9113D45ULL, // 772 0x528EF88AB6AC0A0DULL, 0xA09EA253597BE3FFULL, // 774 0x430DDFB3AC48CD56ULL, 0xC4B3A67AF45CE46FULL, // 776 0x4ECECFD8FBE2D05EULL, 0x3EF56F10B39935F0ULL, // 778 0x0B22D6829CD619C6ULL, 0x17FD460A74DF2069ULL, // 780 0x6CF8CC8E8510ED40ULL, 0xD6C824BF3A6ECAA7ULL, // 782 0x61243D581A817049ULL, 0x048BACB6BBC163A2ULL, // 784 0xD9A38AC27D44CC32ULL, 0x7FDDFF5BAAF410ABULL, // 786 0xAD6D495AA804824BULL, 0xE1A6A74F2D8C9F94ULL, // 788 0xD4F7851235DEE8E3ULL, 0xFD4B7F886540D893ULL, // 790 0x247C20042AA4BFDAULL, 0x096EA1C517D1327CULL, // 792 0xD56966B4361A6685ULL, 0x277DA5C31221057DULL, // 794 0x94D59893A43ACFF7ULL, 0x64F0C51CCDC02281ULL, // 796 0x3D33BCC4FF6189DBULL, 0xE005CB184CE66AF1ULL, // 798 0xFF5CCD1D1DB99BEAULL, 0xB0B854A7FE42980FULL, // 800 0x7BD46A6A718D4B9FULL, 0xD10FA8CC22A5FD8CULL, // 802 0xD31484952BE4BD31ULL, 0xC7FA975FCB243847ULL, // 804 0x4886ED1E5846C407ULL, 0x28CDDB791EB70B04ULL, // 806 0xC2B00BE2F573417FULL, 0x5C9590452180F877ULL, // 808 0x7A6BDDFFF370EB00ULL, 0xCE509E38D6D9D6A4ULL, // 810 0xEBEB0F00647FA702ULL, 0x1DCC06CF76606F06ULL, // 812 0xE4D9F28BA286FF0AULL, 0xD85A305DC918C262ULL, // 814 0x475B1D8732225F54ULL, 0x2D4FB51668CCB5FEULL, // 816 0xA679B9D9D72BBA20ULL, 0x53841C0D912D43A5ULL, // 818 0x3B7EAA48BF12A4E8ULL, 0x781E0E47F22F1DDFULL, // 820 0xEFF20CE60AB50973ULL, 0x20D261D19DFFB742ULL, // 822 0x16A12B03062A2E39ULL, 0x1960EB2239650495ULL, // 824 0x251C16FED50EB8B8ULL, 0x9AC0C330F826016EULL, // 826 0xED152665953E7671ULL, 0x02D63194A6369570ULL, // 828 0x5074F08394B1C987ULL, 0x70BA598C90B25CE1ULL, // 830 0x794A15810B9742F6ULL, 0x0D5925E9FCAF8C6CULL, // 832 0x3067716CD868744EULL, 0x910AB077E8D7731BULL, // 834 0x6A61BBDB5AC42F61ULL, 0x93513EFBF0851567ULL, // 836 0xF494724B9E83E9D5ULL, 0xE887E1985C09648DULL, // 838 0x34B1D3C675370CFDULL, 0xDC35E433BC0D255DULL, // 840 0xD0AAB84234131BE0ULL, 0x08042A50B48B7EAFULL, // 842 0x9997C4EE44A3AB35ULL, 0x829A7B49201799D0ULL, // 844 0x263B8307B7C54441ULL, 0x752F95F4FD6A6CA6ULL, // 846 0x927217402C08C6E5ULL, 0x2A8AB754A795D9EEULL, // 848 0xA442F7552F72943DULL, 0x2C31334E19781208ULL, // 850 0x4FA98D7CEAEE6291ULL, 0x55C3862F665DB309ULL, // 852 0xBD0610175D53B1F3ULL, 0x46FE6CB840413F27ULL, // 854 0x3FE03792DF0CFA59ULL, 0xCFE700372EB85E8FULL, // 856 0xA7BE29E7ADBCE118ULL, 0xE544EE5CDE8431DDULL, // 858 0x8A781B1B41F1873EULL, 0xA5C94C78A0D2F0E7ULL, // 860 0x39412E2877B60728ULL, 0xA1265EF3AFC9A62CULL, // 862 0xBCC2770C6A2506C5ULL, 0x3AB66DD5DCE1CE12ULL, // 864 0xE65499D04A675B37ULL, 0x7D8F523481BFD216ULL, // 866 0x0F6F64FCEC15F389ULL, 0x74EFBE618B5B13C8ULL, // 868 0xACDC82B714273E1DULL, 0xDD40BFE003199D17ULL, // 870 0x37E99257E7E061F8ULL, 0xFA52626904775AAAULL, // 872 0x8BBBF63A463D56F9ULL, 0xF0013F1543A26E64ULL, // 874 0xA8307E9F879EC898ULL, 0xCC4C27A4150177CCULL, // 876 0x1B432F2CCA1D3348ULL, 0xDE1D1F8F9F6FA013ULL, // 878 0x606602A047A7DDD6ULL, 0xD237AB64CC1CB2C7ULL, // 880 0x9B938E7225FCD1D3ULL, 0xEC4E03708E0FF476ULL, // 882 0xFEB2FBDA3D03C12DULL, 0xAE0BCED2EE43889AULL, // 884 0x22CB8923EBFB4F43ULL, 0x69360D013CF7396DULL, // 886 0x855E3602D2D4E022ULL, 0x073805BAD01F784CULL, // 888 0x33E17A133852F546ULL, 0xDF4874058AC7B638ULL, // 890 0xBA92B29C678AA14AULL, 0x0CE89FC76CFAADCDULL, // 892 0x5F9D4E0908339E34ULL, 0xF1AFE9291F5923B9ULL, // 894 0x6E3480F60F4A265FULL, 0xEEBF3A2AB29B841CULL, // 896 0xE21938A88F91B4ADULL, 0x57DFEFF845C6D3C3ULL, // 898 0x2F006B0BF62CAAF2ULL, 0x62F479EF6F75EE78ULL, // 900 0x11A55AD41C8916A9ULL, 0xF229D29084FED453ULL, // 902 0x42F1C27B16B000E6ULL, 0x2B1F76749823C074ULL, // 904 0x4B76ECA3C2745360ULL, 0x8C98F463B91691BDULL, // 906 0x14BCC93CF1ADE66AULL, 0x8885213E6D458397ULL, // 908 0x8E177DF0274D4711ULL, 0xB49B73B5503F2951ULL, // 910 0x10168168C3F96B6BULL, 0x0E3D963B63CAB0AEULL, // 912 0x8DFC4B5655A1DB14ULL, 0xF789F1356E14DE5CULL, // 914 0x683E68AF4E51DAC1ULL, 0xC9A84F9D8D4B0FD9ULL, // 916 0x3691E03F52A0F9D1ULL, 0x5ED86E46E1878E80ULL, // 918 0x3C711A0E99D07150ULL, 0x5A0865B20C4E9310ULL, // 920 0x56FBFC1FE4F0682EULL, 0xEA8D5DE3105EDF9BULL, // 922 0x71ABFDB12379187AULL, 0x2EB99DE1BEE77B9CULL, // 924 0x21ECC0EA33CF4523ULL, 0x59A4D7521805C7A1ULL, // 926 0x3896F5EB56AE7C72ULL, 0xAA638F3DB18F75DCULL, // 928 0x9F39358DABE9808EULL, 0xB7DEFA91C00B72ACULL, // 930 0x6B5541FD62492D92ULL, 0x6DC6DEE8F92E4D5BULL, // 932 0x353F57ABC4BEEA7EULL, 0x735769D6DA5690CEULL, // 934 0x0A234AA642391484ULL, 0xF6F9508028F80D9DULL, // 936 0xB8E319A27AB3F215ULL, 0x31AD9C1151341A4DULL, // 938 0x773C22A57BEF5805ULL, 0x45C7561A07968633ULL, // 940 0xF913DA9E249DBE36ULL, 0xDA652D9B78A64C68ULL, // 942 0x4C27A97F3BC334EFULL, 0x76621220E66B17F4ULL, // 944 0x967743899ACD7D0BULL, 0xF3EE5BCAE0ED6782ULL, // 946 0x409F753600C879FCULL, 0x06D09A39B5926DB6ULL, // 948 0x6F83AEB0317AC588ULL, 0x01E6CA4A86381F21ULL, // 950 0x66FF3462D19F3025ULL, 0x72207C24DDFD3BFBULL, // 952 0x4AF6B6D3E2ECE2EBULL, 0x9C994DBEC7EA08DEULL, // 954 0x49ACE597B09A8BC4ULL, 0xB38C4766CF0797BAULL, // 956 0x131B9373C57C2A75ULL, 0xB1822CCE61931E58ULL, // 958 0x9D7555B909BA1C0CULL, 0x127FAFDD937D11D2ULL, // 960 0x29DA3BADC66D92E4ULL, 0xA2C1D57154C2ECBCULL, // 962 0x58C5134D82F6FE24ULL, 0x1C3AE3515B62274FULL, // 964 0xE907C82E01CB8126ULL, 0xF8ED091913E37FCBULL, // 966 0x3249D8F9C80046C9ULL, 0x80CF9BEDE388FB63ULL, // 968 0x1881539A116CF19EULL, 0x5103F3F76BD52457ULL, // 970 0x15B7E6F5AE47F7A8ULL, 0xDBD7C6DED47E9CCFULL, // 972 0x44E55C410228BB1AULL, 0xB647D4255EDB4E99ULL, // 974 0x5D11882BB8AAFC30ULL, 0xF5098BBB29D3212AULL, // 976 0x8FB5EA14E90296B3ULL, 0x677B942157DD025AULL, // 978 0xFB58E7C0A390ACB5ULL, 0x89D3674C83BD4A01ULL, // 980 0x9E2DA4DF4BF3B93BULL, 0xFCC41E328CAB4829ULL, // 982 0x03F38C96BA582C52ULL, 0xCAD1BDBD7FD85DB2ULL, // 984 0xBBB442C16082AE83ULL, 0xB95FE86BA5DA9AB0ULL, // 986 0xB22E04673771A93FULL, 0x845358C9493152D8ULL, // 988 0xBE2A488697B4541EULL, 0x95A2DC2DD38E6966ULL, // 990 0xC02C11AC923C852BULL, 0x2388B1990DF2A87BULL, // 992 0x7C8008FA1B4F37BEULL, 0x1F70D0C84D54E503ULL, // 994 0x5490ADEC7ECE57D4ULL, 0x002B3C27D9063A3AULL, // 996 0x7EAEA3848030A2BFULL, 0xC602326DED2003C0ULL, // 998 0x83A7287D69A94086ULL, 0xC57A5FCB30F57A8AULL, // 1000 0xB56844E479EBE779ULL, 0xA373B40F05DCBCE9ULL, // 1002 0xD71A786E88570EE2ULL, 0x879CBACDBDE8F6A0ULL, // 1004 0x976AD1BCC164A32FULL, 0xAB21E25E9666D78BULL, // 1006 0x901063AAE5E5C33CULL, 0x9818B34448698D90ULL, // 1008 0xE36487AE3E1E8ABBULL, 0xAFBDF931893BDCB4ULL, // 1010 0x6345A0DC5FBBD519ULL, 0x8628FE269B9465CAULL, // 1012 0x1E5D01603F9C51ECULL, 0x4DE44006A15049B7ULL, // 1014 0xBF6C70E5F776CBB1ULL, 0x411218F2EF552BEDULL, // 1016 0xCB0C0708705A36A3ULL, 0xE74D14754F986044ULL, // 1018 0xCD56D9430EA8280EULL, 0xC12591D7535F5065ULL, // 1020 0xC83223F1720AEF96ULL, 0xC3A0396F7363A51FULL, // 1022 }; static inline void round(uint64_t& a, uint64_t& b, uint64_t& c, uint64_t x, int mul) { c ^= x; a -= table[uint8_t(c >> 0) + 0 * 256] ^ table[uint8_t(c >> 16) + 1 * 256] ^ table[uint8_t(c >> 32) + 2 * 256] ^ table[uint8_t(c >> 48) + 3 * 256]; b += table[uint8_t(c >> 8) + 3 * 256] ^ table[uint8_t(c >> 24) + 2 * 256] ^ table[uint8_t(c >> 40) + 1 * 256] ^ table[uint8_t(c >> 56) + 0 * 256]; b *= mul; } static void tiger_compress(const uint8_t* str, uint64_t state[3]) { uint64_t a = state[0]; uint64_t b = state[1]; uint64_t c = state[2]; uint64_t x0 = Endian::read_UA_L64(str + 0); uint64_t x1 = Endian::read_UA_L64(str + 8); uint64_t x2 = Endian::read_UA_L64(str + 16); uint64_t x3 = Endian::read_UA_L64(str + 24); uint64_t x4 = Endian::read_UA_L64(str + 32); uint64_t x5 = Endian::read_UA_L64(str + 40); uint64_t x6 = Endian::read_UA_L64(str + 48); uint64_t x7 = Endian::read_UA_L64(str + 56); uint64_t aa = a; uint64_t bb = b; uint64_t cc = c; round(a, b, c, x0, 5); round(b, c, a, x1, 5); round(c, a, b, x2, 5); round(a, b, c, x3, 5); round(b, c, a, x4, 5); round(c, a, b, x5, 5); round(a, b, c, x6, 5); round(b, c, a, x7, 5); x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5LL; x1 ^= x0; x2 += x1; x3 -= x2 ^ ((~x1) << 19); x4 ^= x3; x5 += x4; x6 -= x5 ^ ((~x4) >> 23); x7 ^= x6; x0 += x7; x1 -= x0 ^ ((~x7) << 19); x2 ^= x1; x3 += x2; x4 -= x3 ^ ((~x2) >> 23); x5 ^= x4; x6 += x5; x7 -= x6 ^ 0x0123456789ABCDEFLL; round(c, a, b, x0, 7); round(a, b, c, x1, 7); round(b, c, a, x2, 7); round(c, a, b, x3, 7); round(a, b, c, x4, 7); round(b, c, a, x5, 7); round(c, a, b, x6, 7); round(a, b, c, x7, 7); x0 -= x7 ^ 0xA5A5A5A5A5A5A5A5LL; x1 ^= x0; x2 += x1; x3 -= x2 ^ ((~x1) << 19); x4 ^= x3; x5 += x4; x6 -= x5 ^ ((~x4) >> 23); x7 ^= x6; x0 += x7; x1 -= x0 ^ ((~x7) << 19); x2 ^= x1; x3 += x2; x4 -= x3 ^ ((~x2) >> 23); x5 ^= x4; x6 += x5; x7 -= x6 ^ 0x0123456789ABCDEFLL; round(b, c, a, x0, 9); round(c, a, b, x1, 9); round(a, b, c, x2, 9); round(b, c, a, x3, 9); round(c, a, b, x4, 9); round(a, b, c, x5, 9); round(b, c, a, x6, 9); round(c, a, b, x7, 9); state[0] = a ^ aa; state[1] = b - bb; state[2] = c + cc; } static inline void initState(uint64_t state[3]) { state[0] = 0x0123456789ABCDEFULL; state[1] = 0xFEDCBA9876543210ULL; state[2] = 0xF096A5B4C3B2E187ULL; } static inline void returnState(uint64_t state[3]) { if (OPENMSX_BIGENDIAN) { state[0] = Endian::bswap64(state[0]); state[1] = Endian::bswap64(state[1]); state[2] = Endian::bswap64(state[2]); } } void tiger(const uint8_t* str, size_t length, TigerHash& result) { uint8_t temp[64]; initState(result.h64); size_t i; for (i = length; i >= 64; i -= 64) { tiger_compress(str, result.h64); str += 64; } size_t j; for (j = 0; j < i; ++j) { temp[j] = str[j]; } temp[j++] = 0x01; for (/**/; j & 7; ++j) { temp[j] = 0; } if (j > 56) { for (/**/; j < 64; ++j) { temp[j] = 0; } tiger_compress(temp, result.h64); j = 0; } for (/**/; j < 56; ++j) { temp[j] = 0; } Endian::write_UA_L64(temp + 56, uint64_t(length) << 3); tiger_compress(temp, result.h64); returnState(result.h64); } void tiger_int(const TigerHash& h0, const TigerHash& h1, TigerHash& result) { static uint8_t buf[64] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; memcpy(buf + 1, h0.h64, 24); memcpy(buf + 1 + 24, h1.h64, 24); initState(result.h64); tiger_compress(buf, result.h64); returnState(result.h64); } void tiger_leaf(/*const*/ uint8_t data[1024], TigerHash& result) { static uint8_t last[64] = { 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; initState(result.h64); auto backup = data[-1]; data[-1] = 0; for (int i = 0; i < 16; ++i) { tiger_compress(data - 1 + i * 64, result.h64); } data[-1] = backup; last[0] = data[1023]; tiger_compress(last, result.h64); returnState(result.h64); } } // namespace openmsx openmsx-0.10.0/src/utils/array_ref.hh0000644000175000017500000000565012262345041020274 0ustar manuelmanuel00000000000000#ifndef ARRAY_REF_HH #define ARRAY_REF_HH #include #include #include #include #include /** This class implements a subset of the proposal for std::array_ref * (proposed for the next c++ standard (c++1y)). * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3334.html#classstd_1_1array__ref * * It has an interface that is close to std::vector, but it does not own the * memory of the array. Basically it's just a wrapper around a pointer plus * length. */ template class array_ref { public: // types typedef T value_type; typedef const T* pointer; typedef const T& reference; typedef const T& const_reference; typedef const T* const_iterator; typedef const_iterator iterator; typedef std::reverse_iterator const_reverse_iterator; typedef const_reverse_iterator reverse_iterator; typedef size_t size_type; typedef ptrdiff_t difference_type; // construct/copy/assign array_ref() : dat(nullptr), siz(0) {} array_ref(const array_ref& r) : dat(r.data()), siz(r.size()) {} array_ref(const T* array, size_t length) : dat(array), siz(length) {} array_ref(const std::vector& v) : dat(v.data()), siz(v.size()) {} template array_ref(const T(&a)[N]) : dat(a), siz(N) {} array_ref& operator=(const array_ref& rhs) { dat = rhs.data(); siz = rhs.size(); return *this; } // iterators const_iterator begin() const { return dat; } const_iterator end() const { return dat + siz; } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } // capacity size_type size() const { return siz; } bool empty() const { return siz == 0; } // element access const T& operator[](size_t i) const { assert(i < siz); return dat[i]; } const T& front() const { return (*this)[0]; } const T& back() const { return (*this)[siz - 1]; } const T* data() const { return dat; } // mutators void clear() { siz = 0; } // no need to change 'dat' void remove_prefix(size_type n) { if (n <= siz) { dat += n; siz -= n; } else { clear(); } } void remove_suffix(size_type n) { if (n <= siz) { siz -= n; } else { clear(); } } void pop_back() { remove_suffix(1); } void pop_front() { remove_prefix(1); } array_ref substr(size_type pos, size_type n = size_type(-1)) const { if (pos >= siz) return array_ref(); return array_ref(dat + pos, std::min(n, siz - pos)); } private: const T* dat; size_type siz; }; // deducing constructor wrappers template inline array_ref make_array_ref(const T* array, size_t length) { return array_ref(array, length); } template inline array_ref make_array_ref(const T(&a)[N]) { return array_ref(a); } template inline array_ref make_array_ref(const std::vector& v) { return array_ref(v); } #endif openmsx-0.10.0/src/utils/Base64.hh0000644000175000017500000000027012262345041017337 0ustar manuelmanuel00000000000000#ifndef BASE64_HH #define BASE64_HH #include namespace Base64 { std::string encode(const void* input, size_t len); std::string decode(const std::string& input); } #endif openmsx-0.10.0/src/utils/StringMap.cc0000644000175000017500000001357512262345041020221 0ustar manuelmanuel00000000000000#include "StringMap.hh" // Hash function for strings // Fowler / Noll / Vo (FNV-1a) Hash // http://www.isthe.com/chongo/tech/comp/fnv/ static inline unsigned hashString(string_ref str) { unsigned hash = 2166136261u; for (unsigned i = 0; i < str.size(); ++i) { hash ^= str[i]; hash *= 16777619; } return hash; } StringMapImpl::StringMapImpl(unsigned itemSize_, unsigned initSize) : itemSize(itemSize_) { if (initSize) { // If a size is specified, initialize the table with that many buckets. init(initSize); } else { // Otherwise, initialize it with zero buckets to avoid the allocation. theTable = nullptr; numBuckets = 0; numItems = 0; numTombstones = 0; } } void StringMapImpl::init(unsigned initSize) { assert(((initSize & (initSize - 1)) == 0) && "Init Size must be a power of 2 or zero!"); numBuckets = initSize; numItems = 0; numTombstones = 0; theTable = static_cast(calloc( numBuckets + 1, sizeof(StringMapEntryBase**) + sizeof(unsigned))); if (unlikely(!theTable)) { throw std::bad_alloc(); } // Allocate one extra bucket, set it to look filled so the iterators // stop at end. theTable[numBuckets] = reinterpret_cast(2); } unsigned StringMapImpl::lookupBucketFor(string_ref name) { if (numBuckets == 0) { // Hash table unallocated so far? init(16); } unsigned fullHashValue = hashString(name); unsigned bucketNo = fullHashValue & (numBuckets - 1); unsigned* hashTable = getHashTable(); unsigned probeAmt = 1; int firstTombstone = -1; while (true) { StringMapEntryBase* bucketItem = theTable[bucketNo]; if (!bucketItem) { // Empty bucket, this means the key isn't in the table // yet. If we found a tombstone earlier, then reuse // that instead of using this empty bucket. if (firstTombstone != -1) { hashTable[firstTombstone] = fullHashValue; return firstTombstone; } hashTable[bucketNo] = fullHashValue; return bucketNo; } else if (bucketItem == getTombstoneVal()) { // Skip tombstones, but remember the first one we see. if (firstTombstone == -1) firstTombstone = bucketNo; } else if (hashTable[bucketNo] == fullHashValue) { // If the full hash value matches, check deeply for a // match. The common case here is that we are only // looking at the buckets (for item info being non-nullptr // and for the full hash value) not at the items. This // is important for cache locality. auto itemStr = reinterpret_cast(bucketItem) + itemSize; if (name == string_ref(itemStr, bucketItem->getKeyLength())) { return bucketNo; } } // Use quadratic probing, it has fewer clumping artifacts than linear // probing and has good cache behavior in the common case. bucketNo = (bucketNo + probeAmt) & (numBuckets - 1); ++probeAmt; } } int StringMapImpl::findKey(string_ref key) const { if (numBuckets == 0) return -1; unsigned fullHashValue = hashString(key); unsigned bucketNo = fullHashValue & (numBuckets - 1); unsigned* hashTable = getHashTable(); unsigned probeAmt = 1; while (true) { StringMapEntryBase* bucketItem = theTable[bucketNo]; if (!bucketItem) { // Empty bucket, key isn't in the table yet. return -1; } else if (bucketItem == getTombstoneVal()) { // Ignore tombstones. } else if (hashTable[bucketNo] == fullHashValue) { // Hash matches, compare full string. auto itemStr = reinterpret_cast(bucketItem) + itemSize; if (key == string_ref(itemStr, bucketItem->getKeyLength())) { return bucketNo; } } // Quadratic probing. bucketNo = (bucketNo + probeAmt) & (numBuckets - 1); ++probeAmt; } } void StringMapImpl::removeKey(StringMapEntryBase* v) { auto vStr = reinterpret_cast(v) + itemSize; StringMapEntryBase* v2 = removeKey(string_ref(vStr, v->getKeyLength())); assert(v == v2 && "Didn't find key?"); (void)v2; } StringMapEntryBase* StringMapImpl::removeKey(string_ref key) { int bucket = findKey(key); if (bucket == -1) return nullptr; StringMapEntryBase* result = theTable[bucket]; theTable[bucket] = getTombstoneVal(); --numItems; ++numTombstones; assert(numItems + numTombstones <= numBuckets); return result; } void StringMapImpl::rehashTable() { // If the hash table is now more than 3/4 full, or if fewer than 1/8 of // the buckets are empty (meaning that many are filled with tombstones), // grow/rehash the table. unsigned newSize; if ((numItems * 4) > (numBuckets * 3)) { newSize = numBuckets * 2; // double size } else if (numBuckets - (numItems + numTombstones) < (numBuckets / 8)) { newSize = numBuckets; // same size, only clear tombstones } else { return; } // Allocate one extra bucket (see init()). auto newTableArray = static_cast( calloc(newSize + 1, sizeof(StringMapEntryBase*) + sizeof(unsigned))); if (unlikely(!newTableArray)) { throw std::bad_alloc(); } newTableArray[newSize] = reinterpret_cast(2); auto newHashArray = reinterpret_cast(newTableArray + newSize + 1); // Rehash all the items into their new buckets. Luckily we already have // the hash values available, so we don't have to rehash any strings. unsigned* hashTable = getHashTable(); for (unsigned i = 0; i != numBuckets; ++i) { StringMapEntryBase* bucket = theTable[i]; if (bucket && (bucket != getTombstoneVal())) { unsigned fullHash = hashTable[i]; unsigned newBucket = fullHash & (newSize - 1); if (!newTableArray[newBucket]) { // Fast case, bucket available. newTableArray[newBucket] = bucket; newHashArray [newBucket] = fullHash; } else { // Otherwise probe for a spot (quadratic). unsigned probeSize = 1; do { newBucket = (newBucket + probeSize++) & (newSize - 1); } while (newTableArray[newBucket]); newTableArray[newBucket] = bucket; newHashArray[newBucket] = fullHash; } } } free(theTable); theTable = newTableArray; numBuckets = newSize; numTombstones = 0; } openmsx-0.10.0/src/utils/node.mk0000644000175000017500000000134612262345041017255 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ Base64 HexDump \ CRC16 \ CircularBuffer \ SerializeBuffer \ Date \ Math \ MemoryOps \ StringMap \ StringOp \ sha1 \ tiger \ TigerTree \ uint128 \ DivModBySame \ AltSpaceSuppressor sdlwin32 utf8_checked win32-dirent win32-arggen \ snappy \ string_ref \ rapidsax \ HDR_ONLY:= \ FixedPoint \ KeyRange \ AlignedBuffer \ MemBuffer \ Subject Observer \ ScopedAssign \ checked_cast \ endian \ alignof \ array_ref \ likely \ inline \ memory \ noncopyable \ unreachable \ stl \ TypeInfo \ type_traits \ DivModByConst \ utf8_core \ utf8_unchecked \ aligned cstdiop cstdlibp direntp statp stringsp unistdp vla win32-arggen \ countof \ xrange include build/node-end.mk openmsx-0.10.0/src/utils/StringMap.hh0000644000175000017500000002645412262345041020233 0ustar manuelmanuel00000000000000#ifndef STRINGMAP_HH #define STRINGMAP_HH // This is based on the StringMap class in LLVM: // http://llvm.org/docs/ProgrammersManual.html#dss_stringmap // See also that page for a more detailed descrption. // // Very briefly: the class StringMap offers a hashtable-based map. The keys // are strings (or any byte-array with variable length) and the value can be // any type. Both keys and values are stored in (=owned by) the map. // // The interface of the StringMap class is very close to the STL // std::map class. Though one notable difference is the use of // 'first'. An example: // // typedef std::map MySTLmap; // MySTLmap m1 = ...; // MySTLmap::const_iterator it1 = m1.find("abc"); // *1* // assert(it1->first == "abc"); // int i1 = it1->second; // // typedef StringMap MyHashmap; // MyHashmap m2 = ...; // MyHashmap::const_iterator it = m2.find("abc"); // *2 // assert(it2->first() == "abc"); // int i2 = it2->second; // // So basically you only have to replace 'first' with 'first()'. Note that on // the line marked with *1* a temporary std::string object needs to be // constructed and destructed. The corresponding line *2* has no such overhead. #include "string_ref.hh" #include "likely.hh" #include #include // memcpy #include // malloc, free #include // bad_alloc template class StringMapConstIterator; template class StringMapIterator; // Non-templatized base class of StringMapEntry. class StringMapEntryBase { public: explicit StringMapEntryBase(unsigned len) : strLen(len) {} unsigned getKeyLength() const { return strLen; } private: unsigned strLen; }; /// Non-templatized base class of StringMap. class StringMapImpl { public: static StringMapEntryBase* getTombstoneVal() { return reinterpret_cast(-1); } unsigned getNumBuckets() const { return numBuckets; } bool empty() const { return numItems == 0; } unsigned size() const { return numItems; } protected: explicit StringMapImpl(unsigned itemSize, unsigned initSize); // Look up the bucket that the specified string should end up in. If it // already exists as a key in the map, the Item pointer for the // specified bucket will be non-null. Otherwise, it will be null. In // either case, the FullHashValue field of the bucket will be set to // the hash value of the string. unsigned lookupBucketFor(string_ref key); // Look up the bucket that contains the specified key. If it exists in // the map, return the bucket number of the key. Otherwise return -1. // This does not modify the map. int findKey(string_ref key) const; // Remove the specified StringMapEntry from the table, but do not // delete it. This aborts if the value isn't in the table. void removeKey(StringMapEntryBase *V); // Remove the StringMapEntry for the specified key from the table, // returning it. If the key is not in the table, this returns null. StringMapEntryBase* removeKey(string_ref key); // Grow the table, redistributing values into the buckets with the // appropriate mod-of-hashtable-size. void rehashTable(); private: void init(unsigned size); unsigned* getHashTable() const { return reinterpret_cast(theTable + numBuckets + 1); } protected: // Array of numBuckets pointers to entries, nullptrs are holes. // theTable[numBuckets] contains a sentinel value for easy iteration. // Followed by an array of the actual hash values as unsigned integers. StringMapEntryBase** theTable; unsigned numBuckets; unsigned numItems; unsigned numTombstones; private: const unsigned itemSize; }; // This is used to represent one value that is inserted into a StringMap. // It contains the value itself and the key (string-length + string-data). template class StringMapEntry : public StringMapEntryBase { public: T second; public: // Create a StringMapEntry for the specified key and value. static StringMapEntry* create(string_ref key, T v) { // Allocate memory. auto newItem = static_cast( malloc(sizeof(StringMapEntry) + key.size())); if (unlikely(!newItem)) { throw std::bad_alloc(); } // Construct the value (using placement new). new (newItem) StringMapEntry(unsigned(key.size()), std::move(v)); // Copy the string data. auto strBuffer = const_cast(newItem->getKeyData()); memcpy(strBuffer, key.data(), key.size()); return newItem; } // Destroy this StringMapEntry, releasing memory. void destroy() { this->~StringMapEntry(); free(this); } string_ref getKey() const { return string_ref(getKeyData(), getKeyLength()); } string_ref first() const { return getKey(); } const T& getValue() const { return second; } T& getValue() { return second; } void setValue(const T& v) { second = v; } // Return the start of the string data that is the key for this value. const char* getKeyData() const { // The string data is stored immediately after this object. return reinterpret_cast(this + 1); } // Given a value that is known to be embedded into a StringMapEntry, // return the StringMapEntry itself. static StringMapEntry& GetStringMapEntryFromValue(T& v) { StringMapEntry* ePtr = 0; auto ptr = reinterpret_cast(&v) - (reinterpret_cast(&ePtr->second) - reinterpret_cast(ePtr)); return *reinterpret_cast(ptr); } static const StringMapEntry& GetStringMapEntryFromValue(const T &v) { return GetStringMapEntryFromValue(const_cast(v)); } // Given key data that is known to be embedded into a StringMapEntry, // return the StringMapEntry itself. static StringMapEntry& GetStringMapEntryFromKeyData(const char* keyData) { auto ptr = const_cast(keyData) - sizeof(StringMapEntry); return *reinterpret_cast(ptr); } private: StringMapEntry(unsigned strLen, T v) : StringMapEntryBase(strLen), second(std::move(v)) {} ~StringMapEntry() {} }; // This is an unconventional map that is specialized for handling keys that are // "strings", which are basically ranges of bytes. This does some funky memory // allocation and hashing things to make it extremely efficient, storing the // string data *after* the value in the map. template class StringMap : public StringMapImpl { public: typedef const char* key_type; typedef T mapped_type; typedef StringMapEntry value_type; typedef size_t size_type; typedef StringMapConstIterator const_iterator; typedef StringMapIterator iterator; explicit StringMap(unsigned initialSize = 0) : StringMapImpl(sizeof(value_type), initialSize) {} ~StringMap() { clear(); free(theTable); } iterator begin() { return iterator(theTable, numBuckets != 0); } const_iterator begin() const { return const_iterator(theTable, numBuckets != 0); } iterator end() { return iterator(theTable + numBuckets); } const_iterator end() const { return const_iterator(theTable + numBuckets); } iterator find(string_ref key) { int bucket = findKey(key); if (bucket == -1) return end(); return iterator(theTable + bucket); } const_iterator find(string_ref key) const { int bucket = findKey(key); if (bucket == -1) return end(); return const_iterator(theTable + bucket); } // Return the entry for the specified key, or a default constructed // value if no such entry exists. T lookup(string_ref key) const { const_iterator it = find(key); if (it != end()) return it->second; return T(); } T& operator[](string_ref key) { return getOrCreateValue(key).second; } size_type count(string_ref key) const { return (find(key) == end()) ? 0 : 1; } // Insert the specified key/value pair into the map. If the key already // exists in the map, return false and ignore the request, otherwise // insert it and return true. bool insert(value_type* keyValue) { unsigned bucketNo = lookupBucketFor(keyValue->getKey()); StringMapEntryBase*& bucket = theTable[bucketNo]; if (bucket && (bucket != getTombstoneVal())) { return false; // Already exists in map. } if (bucket == getTombstoneVal()) { --numTombstones; } bucket = keyValue; ++numItems; assert(numItems + numTombstones <= numBuckets); rehashTable(); return true; } // Empties out the StringMap void clear() { if (empty()) return; // Zap all values, resetting the keys back to non-present (not // tombstone), which is safe because we're removing all // elements. for (unsigned i = 0; i != numBuckets; ++i) { StringMapEntryBase*& bucket = theTable[i]; if (bucket && (bucket != getTombstoneVal())) { static_cast(bucket)->destroy(); } bucket = nullptr; } numItems = 0; numTombstones = 0; } // Look up the specified key in the table. If a value exists, return // it. Otherwise, default construct a value, insert it, and return. value_type& getOrCreateValue(string_ref key, T val = T()) { unsigned bucketNo = lookupBucketFor(key); StringMapEntryBase*& bucket = theTable[bucketNo]; if (bucket && (bucket != getTombstoneVal())) { return *static_cast(bucket); } value_type* newItem = value_type::create(key, std::move(val)); if (bucket == getTombstoneVal()) --numTombstones; ++numItems; assert(numItems + numTombstones <= numBuckets); // Fill in the bucket for the hash table. The FullHashValue was already // filled in by lookupBucketFor(). bucket = newItem; rehashTable(); return *newItem; } // Remove the specified key/value pair from the map, but do not destroy // it. This aborts if the key is not in the map. void remove(value_type* keyValue) { removeKey(keyValue); } void erase(iterator i) { value_type& v = *i; remove(&v); v.destroy(); } bool erase(string_ref key) { iterator i = find(key); if (i == end()) return false; erase(i); return true; } private: // disable copy and assign StringMap(const StringMap&); StringMap& operator=(const StringMap&); }; template class StringMapConstIterator { public: typedef StringMapEntry value_type; explicit StringMapConstIterator( StringMapEntryBase** bucket, bool advance = false) : ptr(bucket) { if (advance) advancePastEmptyBuckets(); } const value_type& operator*() const { return *static_cast(*ptr); } const value_type* operator->() const { return static_cast(*ptr); } bool operator==(const StringMapConstIterator& rhs) const { return ptr == rhs.ptr; } bool operator!=(const StringMapConstIterator& rhs) const { return ptr != rhs.ptr; } inline StringMapConstIterator& operator++() { // preincrement ++ptr; advancePastEmptyBuckets(); return *this; } StringMapConstIterator operator++(int) { // postincrement StringMapConstIterator tmp = *this; ++*this; return tmp; } protected: StringMapEntryBase** ptr; private: void advancePastEmptyBuckets() { while ((*ptr == nullptr) || (*ptr == StringMapImpl::getTombstoneVal())) { ++ptr; } } }; template class StringMapIterator : public StringMapConstIterator { public: typedef StringMapEntry value_type; explicit StringMapIterator( StringMapEntryBase** bucket, bool advance = false) : StringMapConstIterator(bucket, advance) { } value_type& operator*() const { return *static_cast(*this->ptr); } value_type* operator->() const { return static_cast(*this->ptr); } }; #endif openmsx-0.10.0/src/utils/inline.hh0000644000175000017500000000115312262345041017572 0ustar manuelmanuel00000000000000#ifndef INLINE_HH #define INLINE_HH // This inline trick doesn't work on gcc 3.x when using debug flavour // so we only enable it in the other cases. #if __GNUC__ > 3 || (!defined(DEBUG) && __GNUC__ > 2) #define ALWAYS_INLINE inline __attribute__((always_inline)) #define NEVER_INLINE __attribute__((noinline)) #elif defined _MSC_VER && 0 // Enabling these macros appears to make openmsx about 5% slower // when compiled with VC++ for x64. Hence we leave the defaults #define ALWAYS_INLINE __forceinline #define NEVER_INLINE __declspec(noinline) #else #define ALWAYS_INLINE inline #define NEVER_INLINE #endif #endif openmsx-0.10.0/src/utils/rapidsax.cc0000644000175000017500000003511412262345041020121 0ustar manuelmanuel00000000000000#include "rapidsax.hh" namespace rapidsax { namespace internal { // Whitespace (space \n \r \t) const bool lutWhitespace[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, // 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 7 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F }; // Node name (anything but space \n \r \t / > ? \0) const bool lutNodeName[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Text (i.e. PCDATA) (anything but < \0) const bool lutText[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Text (i.e. PCDATA) that does not require processing when ws normalization is disabled // (anything but < \0 &) const bool lutTextPureNoWs[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Text (i.e. PCDATA) that does not require processing when ws normalizationis is enabled // (anything but < \0 & space \n \r \t) const bool lutTextPureWithWs[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute name (anything but space \n \r \t / < > = ? ! \0) const bool lutAttributeName[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with single quote (anything but ' \0) const bool lutAttributeData1[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with single quote that does not require processing (anything but ' \0 &) const bool lutAttributeData1Pure[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with double quote (anything but " \0) const bool lutAttributeData2[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Attribute data with double quote that does not require processing (anything but " \0 &) const bool lutAttributeData2Pure[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 1 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 2 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 3 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 4 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 5 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 6 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 7 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 8 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 9 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // C 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // D 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // E 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // F }; // Digits (dec and hex, 255 denotes end of numeric character reference) const u8 lutDigits[256] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 0 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 1 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 2 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,255,255,255,255,255,255, // 3 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 4 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 5 255, 10, 11, 12, 13, 14, 15,255,255,255,255,255,255,255,255,255, // 6 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 7 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 8 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // 9 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // A 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // B 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // C 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // D 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, // E 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 // F }; } // namespace internal } // namespace rapidsax openmsx-0.10.0/src/utils/memory.hh0000644000175000017500000001547012262345041017633 0ustar manuelmanuel00000000000000#ifndef MEMORY_HH #define MEMORY_HH #include #include // See this blog for a motivation for the make_unique() function: // http://herbsutter.com/gotw/_102/ // This function will almost certainly be added in the next revision of the // c++ language. // The above blog also gives an implementation for make_unique(). Unfortunately // vs2012 doesn't support variadic templates yet, so for now we have to use // the longer (and less general) version below. #if 0 template std::unique_ptr make_unique(Args&& ...args) { return std::unique_ptr(new T(std::forward(args)...)); } #else // Emulate variadic templates for upto 13 arguments (yes we need a version // with 13 parameters!). template std::unique_ptr make_unique() { return std::unique_ptr(new T()); } template std::unique_ptr make_unique(P1&& p1) { return std::unique_ptr(new T( std::forward(p1))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2), std::forward(p3))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2), std::forward(p3), std::forward(p4))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2), std::forward(p3), std::forward(p4), std::forward(p5))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2), std::forward(p3), std::forward(p4), std::forward(p5), std::forward(p6))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2), std::forward(p3), std::forward(p4), std::forward(p5), std::forward(p6), std::forward(p7))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2), std::forward(p3), std::forward(p4), std::forward(p5), std::forward(p6), std::forward(p7), std::forward(p8))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8, P9&& p9) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2), std::forward(p3), std::forward(p4), std::forward(p5), std::forward(p6), std::forward(p7), std::forward(p8), std::forward(p9))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8, P9&& p9, P10&& p10) { return std::unique_ptr(new T( std::forward(p1), std::forward(p2 ), std::forward(p3), std::forward(p4 ), std::forward(p5), std::forward(p6 ), std::forward(p7), std::forward(p8 ), std::forward(p9), std::forward(p10))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8, P9&& p9, P10&& p10, P11&& p11) { return std::unique_ptr(new T( std::forward(p1 ), std::forward(p2 ), std::forward(p3 ), std::forward(p4 ), std::forward(p5 ), std::forward(p6 ), std::forward(p7 ), std::forward(p8 ), std::forward(p9 ), std::forward(p10), std::forward(p11))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8, P9&& p9, P10&& p10, P11&& p11, P12&& p12) { return std::unique_ptr(new T( std::forward(p1 ), std::forward(p2 ), std::forward(p3 ), std::forward(p4 ), std::forward(p5 ), std::forward(p6 ), std::forward(p7 ), std::forward(p8 ), std::forward(p9 ), std::forward(p10), std::forward(p11), std::forward(p12))); } template std::unique_ptr make_unique(P1&& p1, P2&& p2, P3&& p3, P4&& p4, P5&& p5, P6&& p6, P7&& p7, P8&& p8, P9&& p9, P10&& p10, P11&& p11, P12&& p12, P13&& p13) { return std::unique_ptr(new T( std::forward(p1 ), std::forward(p2 ), std::forward(p3 ), std::forward(p4 ), std::forward(p5 ), std::forward(p6 ), std::forward(p7 ), std::forward(p8 ), std::forward(p9 ), std::forward(p10), std::forward(p11), std::forward(p12), std::forward(p13))); } #endif #endif openmsx-0.10.0/src/utils/win32-arggen.hh0000644000175000017500000000045312262345041020521 0ustar manuelmanuel00000000000000#ifndef WIN32_ARG_GEN_HH #define WIN32_ARG_GEN_HH #ifdef _WIN32 #include "MemBuffer.hh" namespace openmsx { class ArgumentGenerator { public: ~ArgumentGenerator(); char** GetArguments(int& argc); private: MemBuffer argv; }; #endif } // namespace openmsx #endif // WIN32_ARG_GEN_HH openmsx-0.10.0/src/utils/sdlwin32.cc0000644000175000017500000000100712262345041017745 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "sdlwin32.hh" #include "MSXException.hh" #include "StringOp.hh" #include #ifdef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN // For , which defines it carelessly #endif #include namespace openmsx { HWND getSDLWindowHandle() { SDL_SysWMinfo info; SDL_VERSION(&info.version); if (!SDL_GetWMInfo(&info)) { throw MSXException(StringOp::Builder() << "SDL_GetWMInfo failed: " << SDL_GetError()); } return info.window; } } // namespace openmsx #endif openmsx-0.10.0/src/utils/AltSpaceSuppressor.cc0000644000175000017500000001053312262345041022106 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "AltSpaceSuppressor.hh" #include "MSXException.hh" #include "sdlwin32.hh" #include "openmsx.hh" #include // This module exists to fix the following bug: // [ 1206036 ] Windows Control Menu while using ALT // http://sourceforge.net/tracker/index.php?func=detail&aid=1206036&group_id=38274&atid=421861 // // The Windows control menu will pop up by default in a window when the user // presses LALT+SPACE or RALT+SPACE. Programmatically, this shows up to the // application as a series of Windows messages: the respective WM_SYSKEYDOWN // events for ALT and SPACE, followed by a WM_SYSCOMMAND message whose default // processing by the OS results in the control menu itself appearing. // // The fix, at the application level, is to handle that WM_SYSCOMMAND message // (effectively swallowing it) and thus to prevent it from being forwarded to // Win32's DefWindowProc. // // We first attempted to fix this by interacting with SDL's messaging // subsystem. The hope was that by calling SDL_SetEventFilter with a custom // event filter, we could intercept and silence the offending WM_SYSCOMMAND // message. Unfortunately, SDL allows an application listening for // SDL_SYSWMEVENT events to see those messages, but it does _not_ allow the // application to indicate that the event was handled and should not be // processed further. // // More concretely, the SDL code as of 1.2.13 in SDL_dibevents.c (circa line // 250) ignores the return value from the event filter for WM events and // ultimately forwards the message to DefWindowProc anyway. Further, in // SDL_dx5events.c line 532, a comment implies that this is by design: // // It would be better to allow the application to // decide whether or not to blow these off, but the // semantics of SDL_PrivateSysWMEvent() don't allow // the application that choice. // // We filed bug 686 (http://bugzilla.libsdl.org/show_bug.cgi?id=686) on the SDL // developers. If they respond positively, we may want to return to a solution // along those lines. // // Our second attempt at a fix, the one represented by the code below, involves // registering our own Windows proc, filtering out the offending WM_SYSCOMMAND // event, and letting the rest go through. // // This is currently integrated into openmsx inside the SDLVideoSystem class. namespace openmsx { WindowLongPtrStacker::WindowLongPtrStacker(int index, LONG_PTR value) : hWnd(nullptr) , nIndex(index) , newValue(value) { } void WindowLongPtrStacker::Push(HWND hWndArg) { assert(hWndArg); hWnd = hWndArg; SetLastError(0); oldValue = SetWindowLongPtr(hWnd, nIndex, newValue); if (!oldValue && GetLastError()) { throw MSXException("SetWindowLongPtr failed"); } } void WindowLongPtrStacker::Pop() { assert(hWnd); if (oldValue) { SetLastError(0); LONG_PTR removedValue = SetWindowLongPtr(hWnd, nIndex, oldValue); if (!removedValue && GetLastError()) { throw MSXException("SetWindowLongPtr failed"); } assert(removedValue == newValue); } } LONG_PTR WindowLongPtrStacker::GetOldValue() { return oldValue; } WindowLongPtrStacker AltSpaceSuppressor::procStacker( GWLP_WNDPROC, reinterpret_cast(InterceptorWndProc)); void AltSpaceSuppressor::Start(HWND hWnd) { procStacker.Push(hWnd); } void AltSpaceSuppressor::Stop() { procStacker.Pop(); } LRESULT AltSpaceSuppressor::InterceptorWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { //PRT_DEBUG("message = " << message << ", wParam = " << wParam << ", lParam = " << lParam); LRESULT lResult = 0; if (SuppressAltSpace(hWnd, message, wParam, lParam, &lResult)) { // Message processed return lResult; } // Message not processed, use default handling auto nextWndProc = reinterpret_cast(procStacker.GetOldValue()); if (nextWndProc) { // Forward to the window proc we replaced return CallWindowProc(nextWndProc, hWnd, message, wParam, lParam); } // Let the system default window proc handle the message return DefWindowProc(hWnd, message, wParam, lParam); } bool AltSpaceSuppressor::SuppressAltSpace( HWND /*hWnd*/, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* outResult) { if (message == WM_SYSCOMMAND && wParam == SC_KEYMENU && lParam == LPARAM(' ')) { PRT_DEBUG("Suppressed ALT+SPACE message"); *outResult = 0; return true; // processed } return false; // not processed } } // namespace openmsx #endif openmsx-0.10.0/src/utils/uint128.cc0000644000175000017500000000175512262345041017524 0ustar manuelmanuel00000000000000#if defined __x86_64 && !defined _MSC_VER // nothing #else // __x86_64 && !_MSC_VER #include "uint128.hh" uint128& uint128::operator*=(const uint128& b) { uint128 a = *this; uint128 t = b; lo = 0; hi = 0; while (t != 0) { if (t.lo & 1) { *this += a; } a <<= 1; t >>= 1; } return *this; } uint128 uint128::div(const uint128& ds, uint128& remainder) const { uint128 dd = *this; uint128 r = 0; uint128 q = 0; unsigned b = 127; while (r < ds) { r <<= 1; if (dd.bit(b--)) { r.lo |= 1; } } ++b; while (true) { if (r < ds) { if (!(b--)) break; r <<= 1; if (dd.bit(b)) { r.lo |= 1; } } else { r -= ds; q.setBit(b); } } remainder = r; return q; } bool uint128::bit(unsigned n) const { if (n < 64) { return (lo & (1ull << n)) != 0; } else { return (hi & (1ull << (n - 64))) != 0; } } void uint128::setBit(unsigned n) { if (n < 64) { lo |= (1ull << n); } else { hi |= (1ull << (n - 64)); } } #endif // __x86_64 && !_MSC_VER openmsx-0.10.0/src/utils/xrange.hh0000644000175000017500000000357512262345041017612 0ustar manuelmanuel00000000000000#ifndef XRANGE_HH #define XRANGE_HH // Utility to iterate over a range of numbers, // modeled after python's xrange() function. // // Typically used as a more compact notation for some for-loop patterns: // // a) loop over [0, N) // // auto num = expensiveFunctionCall(); // for (decltype(num) i = 0; i < num; ++i) { ... } // // for (auto i : xrange(expensiveFunctionCall()) { ... } // // b) loop over [B, E) // // auto i = func1(); // auto end = func2(); // for (/**/; i < end; ++i) { ... } // // for (auto i : xrange(func1(), func2()) { ... } // // Note that when using the xrange utility you also don't need to worry about // the correct type of the induction variable. // // Gcc is able to optimize the xrange() based loops to the same code as the // equivalent 'old-style' for loop. // // In an earlier version of this utility I also implemented the possibility to // specify a step size (currently the step size is always +1). Although I // believe that code was correct I still removed it because it was quite tricky // (getting the stop condition correct is not trivial) and we don't need it // currently. template class XRange { public: class XRangeIter { public: XRangeIter(T x_) : x(x_) { } T operator*() const { return x; } XRangeIter& operator++() { ++x; return *this; } bool operator==(const XRangeIter& other) const { return x == other.x; } bool operator!=(const XRangeIter& other) const { return x != other.x; } private: T x; }; XRange(T e_) : b(0), e(e_) { } XRange(T b_, T e_) : b(b_), e(e_) { } XRangeIter begin() { return XRangeIter(b); } XRangeIter end() { return XRangeIter(e); } private: const T b; const T e; }; template inline XRange xrange(T e) { return XRange(e); } template inline XRange xrange(T b, T e) { return XRange(b, e); } #endif openmsx-0.10.0/src/utils/TypeInfo.hh0000644000175000017500000000276312262345041020061 0ustar manuelmanuel00000000000000#ifndef TYPEINFO_HH #define TYPEINFO_HH #include #include // Based on Loki::TypeInfo // offers a first-class, comparable wrapper over std::type_info class TypeInfo { public: TypeInfo(); TypeInfo(const std::type_info& ti); // Get the underlying std::type_info const std::type_info& get() const; // Compatibility functions bool before(const TypeInfo& rhs) const; const char* name() const; private: const std::type_info* info; }; inline TypeInfo::TypeInfo() { class Dummy {}; info = &typeid(Dummy); assert(info); } inline TypeInfo::TypeInfo(const std::type_info& ti) : info(&ti) { assert(info); } inline const std::type_info& TypeInfo::get() const { return *info; } inline bool TypeInfo::before(const TypeInfo& rhs) const { // gcc version of std::type_info::before() returns bool return info->before(*rhs.info) != 0; } inline const char* TypeInfo::name() const { return info->name(); } // Comparison operators inline bool operator==(const TypeInfo& lhs, const TypeInfo& rhs) { return lhs.get() == rhs.get(); } inline bool operator<(const TypeInfo& lhs, const TypeInfo& rhs) { return lhs.before(rhs); } inline bool operator!=(const TypeInfo& lhs, const TypeInfo& rhs) { return !(lhs == rhs); } inline bool operator>(const TypeInfo& lhs, const TypeInfo& rhs) { return rhs < lhs; } inline bool operator<=(const TypeInfo& lhs, const TypeInfo& rhs) { return !(lhs > rhs); } inline bool operator>=(const TypeInfo& lhs, const TypeInfo& rhs) { return !(lhs < rhs); } #endif openmsx-0.10.0/src/utils/SerializeBuffer.hh0000644000175000017500000001240712262345041021401 0ustar manuelmanuel00000000000000#ifndef SERIALIZEBUFFER_HH #define SERIALIZEBUFFER_HH #include "openmsx.hh" #include "noncopyable.hh" #include #include #include namespace openmsx { /** Memory output buffer * * Acts as a replacement for std::vector. You can insert data in the * buffer and the buffer will automatically grow. Like std::vector it manages * an internal memory buffer that will automatically reallocate and grow * exponentially. * * This class is much less general than std::vector and optimized for the case * of lots of (small) inserts at the end of the buffer (the main use case of * in-memory savestates). This makes it more efficient than std::vector. * std::vector is far from inefficient, but for savestates this is used A LOT, * so even small improvements matter a lot. */ class OutputBuffer : private noncopyable { public: /** Create an empty output buffer. */ OutputBuffer(); /** Delete the buffer again. The data may not be used anymore. */ ~OutputBuffer(); /** Insert data at the end of this buffer. * This will automatically grow this buffer. */ void insert(const void* __restrict data, size_t len) __restrict { #ifdef __GNUC__ if (__builtin_constant_p(len)) { if (len == 1) { insertN<1>(data); return; } else if (len == 2) { insertN<2>(data); return; } else if (len == 4) { insertN<4>(data); return; } else if (len == 8) { insertN<8>(data); return; } } #endif insertN(data, len); } #ifdef __GNUC__ template void insertN(const void* __restrict data) __restrict; #endif void insertN(const void* __restrict data, size_t len) __restrict; /** Insert data at a given position. This will overwrite the old data. * It's not possible to grow the buffer via this method (so the buffer * must already be big enough to hold the new data). */ void insertAt(size_t pos, const void* __restrict data, size_t len) __restrict { assert(begin + pos + len <= finish); memcpy(begin + pos, data, len); } /** Reserve space to insert the given number of bytes. * The returned pointer is only valid until the next internal * reallocate, so till the next call to insert() or allocate(). * * If you don't know yet exactly how much memory to allocate (e.g. * when the buffer will be used for gzip output data), you can request * the maximum size and deallocate the unused space later. */ byte* allocate(size_t len) { byte* newEnd = end + len; // Make sure the next OutputBuffer will start with an initial size // that can hold this much space plus some slack. size_t newSize = newEnd - begin; lastSize = std::max(lastSize, newSize + 1000); if (newEnd <= finish) { byte* result = end; end = newEnd; return result; } else { return allocateGrow(len); } } /** Free part of a previously allocated buffer. * * The parameter must point right after the last byte of the used * portion of the buffer. So it must be in the range [buf, buf+num] * with buf and num respectively the return value and the parameter * of the last allocate() call. * * See comment in allocate(). This call must be done right after the * allocate() call, there cannot be any other (non-const) call to this * object in between. */ void deallocate(byte* pos) { assert(begin <= pos); assert(pos <= end); end = pos; } /** Get the current size of the buffer. */ size_t getPosition() const { return end - begin; } /** Release ownership of the buffer. * Returns both a pointer to the raw buffer and its size. */ byte* release(size_t& size); private: void insertGrow(const void* __restrict data, size_t len) __restrict; byte* allocateGrow(size_t len) __restrict; byte* begin; // begin of allocated memory byte* end; // points right after the last used byte // so end - begin == size byte* finish; // points right after the last allocated byte // so finish - begin == capacity static size_t lastSize; }; /** This class is complementary to the OutputBuffer class. * Instead of filling an initially empty buffer it starts from a filled buffer * and allows to retrieve items starting from the start of the buffer. */ class InputBuffer : private noncopyable { public: /** Construct new InputBuffer, typically the data and size parameters * will come from a MemBuffer object. */ InputBuffer(const byte* data, size_t size); /** Read the given number of bytes. * This 'consumes' the read bytes, so a future read() will continue * where this read stopped. */ void read(void* __restrict result, size_t len) __restrict { memcpy(result, buf, len); buf += len; assert(buf <= finish); } /** Skip the given number of bytes. * This is similar to a read(), but it will only consume the data, not * copy it. */ void skip(size_t len) { buf += len; assert(buf <= finish); } /** Return a pointer to the current position in the buffer. * This is useful if you don't want to copy the data, but e.g. use it * as input for an uncompress algorithm. You can later use skip() to * actually consume the data. */ const byte* getCurrentPos() const { return buf; } private: const byte* buf; #ifndef NDEBUG const byte* finish; // only used to check asserts #endif }; } // namespace openmsx #endif openmsx-0.10.0/src/utils/cstdiop.hh0000644000175000017500000000025112262345041017757 0ustar manuelmanuel00000000000000#ifndef CSTDIOP_HH #define CSTDIOP_HH #include #ifdef _MSC_VER #include #define STDIN_FILENO _fileno(stdin) #define snprintf _snprintf #endif #endif openmsx-0.10.0/src/utils/string_ref.cc0000644000175000017500000001212512262345041020445 0ustar manuelmanuel00000000000000#include "string_ref.hh" #include "cstdlibp.hh" #include #include using std::string; // Outgoing conversion operators string string_ref::str() const { return siz ? string(dat, siz) : string(); } // mutators void string_ref::remove_prefix(size_type n) { if (n <= siz) { dat += n; siz -= n; } else { clear(); } } void string_ref::remove_suffix(size_type n) { if (n <= siz) { siz -= n; } else { clear(); } } // string operations with the same semantics as std::string int string_ref::compare(string_ref rhs) const { // Check prefix. if (int r = memcmp(dat, rhs.dat, std::min(siz, rhs.siz))) { return r; } // Prefixes match, check length. return int(siz - rhs.siz); // Note: this overflows for very large strings. } string_ref string_ref::substr(size_type pos, size_type n) const { if (pos >= siz) return string_ref(); return string_ref(dat + pos, std::min(n, siz - pos)); } string_ref::size_type string_ref::find(string_ref s) const { // Simple string search algorithm O(size() * s.size()). An algorithm // like Boyer–Moore has better time complexity and will run a lot // faster on large strings. Though when the strings are relatively // short (the typically case?) this very simple algorithm may run // faster (because it has no setup-time). The implementation of // std::string::find() in gcc uses a similar simple algorithm. if (s.empty()) return 0; if (s.size() <= siz) { auto m = siz - s.size(); for (size_type pos = 0; pos <= m; ++pos) { if ((dat[pos] == s[0]) && std::equal(s.begin() + 1, s.end(), dat + pos + 1)) { return pos; } } } return npos; } string_ref::size_type string_ref::find(char c) const { auto it = std::find(begin(), end(), c); return (it == end()) ? npos : it - begin(); } string_ref::size_type string_ref::rfind(string_ref s) const { // see comment in find() if (s.empty()) return siz; if (s.size() <= siz) { auto m = siz - s.size(); for (auto pos = m; pos != size_type(-1); --pos) { if ((dat[pos] == s[0]) && std::equal(s.begin() + 1, s.end(), dat + pos + 1)) { return pos; } } } return npos; } string_ref::size_type string_ref::rfind(char c) const { auto it = std::find(rbegin(), rend(), c); return (it == rend()) ? npos : (it.base() - begin() - 1); } string_ref::size_type string_ref::find_first_of(string_ref s) const { auto it = std::find_first_of(begin(), end(), s.begin(), s.end()); return (it == end()) ? npos : it - begin(); } string_ref::size_type string_ref::find_first_of(char c) const { return find(c); } //string_ref::size_type string_ref::find_first_not_of(string_ref s) const; //string_ref::size_type string_ref::find_first_not_of(char c) const; string_ref::size_type string_ref::find_last_of(string_ref s) const { auto it = std::find_first_of( rbegin(), rend(), s.begin(), s.end()); return (it == rend()) ? npos : (it.base() - begin() - 1); } string_ref::size_type string_ref::find_last_of(char c) const { return rfind(c); } //string_ref::size_type string_ref::find_last_not_of(string_ref s) const; //string_ref::size_type string_ref::find_last_not_of(char c) const; // new string operations (not part of std::string) bool string_ref::starts_with(string_ref x) const { return (siz >= x.size()) && (memcmp(dat, x.data(), x.size()) == 0); } bool string_ref::ends_with(string_ref x) const { return (siz >= x.size()) && (memcmp(dat + siz - x.size(), x.data(), x.size()) == 0); } // Comparison operators bool operator==(string_ref x, string_ref y) { return (x.size() == y.size()) && (memcmp(x.data(), y.data(), x.size()) == 0); } bool operator< (string_ref x, string_ref y) { return x.compare(y) < 0; } // numeric conversions // TODO could be implemented more efficient (don't make a copy) int stoi(string_ref str, string_ref::size_type* idx, int base) { string s = str.str(); const char* begin = s.c_str(); char* end; int result = strtol(begin, &end, base); if (idx) *idx = end - begin; return result; } unsigned long stoul (string_ref str, string_ref::size_type* idx, int base) { string s = str.str(); const char* begin = s.c_str(); char* end; int result = strtoul(begin, &end, base); if (idx) *idx = end - begin; return result; } long long stoll(string_ref str, string_ref::size_type* idx, int base) { string s = str.str(); const char* begin = s.c_str(); char* end; int result = strtoll(begin, &end, base); if (idx) *idx = end - begin; return result; } // concatenation // TODO make s1 + s2 + s3 also efficient string operator+(string_ref x, string_ref y) { string result; result.reserve(x.size() + y.size()); result.append(x.data(), x.size()); result.append(y.data(), y.size()); return result; } std::string operator+(char x, string_ref y) { string result; result.reserve(1 + y.size()); result.append(&x, 1); result.append(y.data(), y.size()); return result; } std::string operator+(string_ref x, char y) { string result; result.reserve(x.size() + 1); result.append(x.data(), x.size()); result.append(&y, 1); return result; } std::ostream& operator<<(std::ostream& os, string_ref str) { os.write(str.data(), str.size()); return os; } openmsx-0.10.0/src/utils/MemBuffer.hh0000644000175000017500000000742512262345041020174 0ustar manuelmanuel00000000000000#ifndef MEMBUFFER_HH #define MEMBUFFER_HH #include "noncopyable.hh" #include #include // for bad_alloc #include #include namespace openmsx { /** This class manages the lifetime of a block of memory. * * Its two main use cases are: * 1) As a safer alternative for new[] / delete[]. * Using this class makes sure the memory block is always properly * cleaned up. Also in case of exceptions. * 2) As an alternative for vector ('byte' or other primitive types). * The main difference with vector is that the allocated block is left * uninitialized. This can be a bit more efficient if the block will * anyway soon be overwritten. * * Like vector this buffer can dynamically grow or shrink. But it's not * optimized for this case (it doesn't keep track of extra capacity). If you * need frequent resizing prefer to use vector instead of this class. */ template class MemBuffer //: private noncopyable { public: /** Construct an empty MemBuffer, no memory is allocated. */ MemBuffer() : dat(nullptr) , sz(0) { } /** Construct a (uninitialized) memory buffer of given size. */ explicit MemBuffer(size_t size) : dat(static_cast(malloc(size * sizeof(T)))) , sz(size) { if (size && !dat) { throw std::bad_alloc(); } } /** Take ownership of the given memory block. This pointer should have * been allocated earlier with malloc() or realloc() (or it should be * nullptr). */ MemBuffer(T* data, size_t size) : dat(data) , sz(size) { } /** Move constructor. */ MemBuffer(MemBuffer&& other) : dat(other.dat) , sz(other.sz) { other.dat = nullptr; } /** Move assignment. */ MemBuffer& operator=(MemBuffer&& other) { std::swap(dat, other.dat); std::swap(sz , other.sz); return *this; } /** Free the memory buffer. */ ~MemBuffer() { free(dat); } /** Returns pointer to the start of the memory buffer. * This method can be called even when there's no buffer allocated. */ const T* data() const { return dat; } T* data() { return dat; } /** Access elements in the memory buffer. */ const T& operator[](size_t i) const { assert(i < sz); return dat[i]; } T& operator[](size_t i) { assert(i < sz); return dat[i]; } /** Returns size of the memory buffer. * The size is in number of elements, not number of allocated bytes. */ size_t size() const { return sz; } /** Same as size() == 0. */ bool empty() const { return sz == 0; } /** Grow or shrink the memory block. * In case of growing, the extra space is left uninitialized. * It is possible (even likely) that the memory buffer is copied * to a new location after this call, so data() will return a * different pointer value. */ void resize(size_t size) { if (size) { auto newDat = static_cast(realloc(dat, size * sizeof(T))); if (!newDat) { throw std::bad_alloc(); } dat = newDat; sz = size; } else { // realloc() can handle zero-size allocactions, // but then we anyway still need to check for // 'size == 0' for the error handling. clear(); } } /** Free the allocated memory block and set the current size to 0. */ void clear() { free(dat); dat = nullptr; sz = 0; } /** STL-style iterators. */ const T* begin() const { return dat; } T* begin() { return dat; } const T* end() const { return dat + sz; } T* end() { return dat + sz; } /** Swap the managed memory block of two MemBuffers. */ void swap(MemBuffer& other) { std::swap(dat, other.dat); std::swap(sz , other.sz ); } private: T* dat; size_t sz; }; } // namespace openmsx namespace std { template void swap(openmsx::MemBuffer& l, openmsx::MemBuffer& r) { l.swap(r); } } #endif openmsx-0.10.0/src/utils/ScopedAssign.hh0000644000175000017500000000060412262345041020676 0ustar manuelmanuel00000000000000#ifndef SCOPEDASSIGN_HH #define SCOPEDASSIGN_HH /** Assign new value to some variable and restore the original value * when this object goes out of scope. */ template class ScopedAssign { public: ScopedAssign(T& var_, T newValue) : var(var_) { oldValue = var; var = newValue; } ~ScopedAssign() { var = oldValue; } private: T& var; T oldValue; }; #endif openmsx-0.10.0/src/utils/likely.hh0000644000175000017500000000074212262345041017610 0ustar manuelmanuel00000000000000#ifndef LIKELY_HH #define LIKELY_HH /* Somewhere in the middle of the GCC 2.96 development cycle, we implemented * a mechanism by which the user can annotate likely branch directions and * expect the blocks to be reordered appropriately. Define __builtin_expect * to nothing for earlier compilers. */ #if __GNUC__ > 2 #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) #else #define likely(x) (x) #define unlikely(x) (x) #endif #endif openmsx-0.10.0/src/utils/snappy.hh0000644000175000017500000000662012262345041017632 0ustar manuelmanuel00000000000000///////////////////////////////////////////////////////////////////////// // // Copyright 2005 and onwards Google Inc. // // 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. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // 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 // OWNER 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. // // A light-weight compression algorithm. It is designed for speed of // compression and decompression, rather than for the utmost in space // savings. // // For getting better compression ratios when you are compressing data // with long repeated sequences or compressing data that is similar to // other data, while still compressing fast, you might look at first // using BMDiff and then compressing the output of BMDiff with // Snappy. // ///////////////////////////////////////////////////////////////////////// // // The snappy code is modified quite a bit for openMSX. The original // verion can be obtained here: // http://code.google.com/p/snappy/ // // The main difference are: // - Rewritten to reuse existing openMSX helper functions and style. // - Removed possibility to operate on chunks of data, the current code // requires the full to-be-(de)compressed memory block in one go. // - Removed all safety checks during decompression. The original code // would return an error on invalid input, this code will crash on // such input (but that shouldn't happen because we only feed input // that was previously produced by the compression routine (and always // keeping that compressed block in memory). // The motivation for this rewrite is to: // - Reduce code duplication between the snappy code and the rest of // openMSX. // - Gain some extra speed at the expense of flexibility (which we don't // need in openMSX). // ///////////////////////////////////////////////////////////////////////// #ifndef SNAPPY_HH #define SNAPPY_HH #include namespace snappy { void compress(const char* input, size_t inLen, char* output, size_t& outLen); void uncompress(const char* input, size_t inLen, char* output, size_t outLen); size_t maxCompressedLength(size_t inLen); } #endif openmsx-0.10.0/src/utils/direntp.hh0000644000175000017500000000130612262345041017761 0ustar manuelmanuel00000000000000#ifndef DIRENTP_HH #define DIRENTP_HH #ifndef _WIN32 #include #else // When building with MinGW, we use our own wrapper instead of the // native dirent.h provided by the MinGW C runtime. We do this because // we need an implementation of dirent that returns UTF8-encoded strings // as chars. // Unfortunately, MinGW's implementation of dirent provides either // wchar_t-based versions that support UTF16 (when UNICODE is defined), // or char-based versions containing what appear to be ANSI strings, // certainly not UTF8. // While this behavior is relatively consistent with Win32, it's not // what we need here. Consequently, we use our wrapper instead. #include "win32-dirent.hh" #endif #endif openmsx-0.10.0/src/utils/DivModBySame.cc0000644000175000017500000000335412262345041020572 0ustar manuelmanuel00000000000000#include "DivModBySame.hh" #include "uint128.hh" #include namespace openmsx { static uint32_t log2(uint64_t i) { uint32_t t = 0; i >>= 1; while (i) { i >>= 1; ++t; } return t; } void DivModBySame::setDivisor(uint32_t divisor_) { //assert(divisor_ < 0x8000000000000000ull); // when divisor is uint64_t divisor = divisor_; // reduce divisor until it becomes odd uint32_t n = 0; uint64_t t = divisor; while (!(t & 1)) { t >>= 1; ++n; } if (t == 1) { m = 0xffffffffffffffffull; a = m; s = 0; } else { // Generate m, s for algorithm 0. Based on: Granlund, T.; Montgomery, // P.L.: "Division by Invariant Integers using Multiplication". // SIGPLAN Notices, Vol. 29, June 1994, page 61. uint32_t l = log2(t) + 1; uint64_t j = 0xffffffffffffffffull % t; uint128 k = (uint128(1) << (64 + l)) / (0xffffffffffffffffull - j); uint128 m_low = (uint128(1) << (64 + l)) / t; uint128 m_high = ((uint128(1) << (64 + l)) + k) / t; while (((m_low >> 1) < (m_high >> 1)) && (l > 0)) { m_low >>= 1; m_high >>= 1; --l; } if ((m_high >> 64) == 0) { m = toUint64(m_high); s = l; a = 0; } else { // Generate m, s for algorithm 1. Based on: Magenheimer, D.J.; et al: // "Integer Multiplication and Division on the HP Precision Architecture". // IEEE Transactions on Computers, Vol 37, No. 8, August 1988, page 980. s = log2(t); uint128 m_low = (uint128(1) << (64 + s)) / t; uint64_t r = toUint64((uint128(1) << (64 + s)) % t); m = toUint64(m_low + ((r <= (t >> 1)) ? 0 : 1)); a = m; } // reduce multiplier to smallest possible while (!(m & 1)) { m >>= 1; a >>= 1; s--; } } // adjust multiplier for reduction of even divisors s += n; } } // namespace openmsx openmsx-0.10.0/src/utils/statp.hh0000644000175000017500000000030312262345041017443 0ustar manuelmanuel00000000000000#ifndef STATP_HH #define STATP_HH #include #ifdef _MSC_VER #define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) #define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) #endif #endif openmsx-0.10.0/src/utils/alignof.hh0000644000175000017500000000122412262345041017732 0ustar manuelmanuel00000000000000#ifndef ALIGNOF_HH #define ALIGNOF_HH // Portable alignof operator // // C++11 has a new alignof operator (much like the sizeof operator). Many // compilers already offered a similar feature in non-c++11 mode as an // extension (e.g. gcc has the __alignof__ operator). This file implements the // same functionality for pre-c++11 compilers in a portable way. Once we switch // to c++11 we can drop this header. // // Usage: // c++11: alignof(SomeType) // this: ALIGNOF(SomeType) template struct AlignOf { struct S { char c; T t; }; static const unsigned value = sizeof(S) - sizeof(T); }; #define ALIGNOF(T) AlignOf::value #endif openmsx-0.10.0/src/utils/cstdlibp.hh0000644000175000017500000000022312262345041020115 0ustar manuelmanuel00000000000000#ifndef CSTDLIBP_HH #define CSTDLIBP_HH #include #ifdef _MSC_VER #define strtoll _strtoi64 #define strtoull _strtoui64 #endif #endif openmsx-0.10.0/src/utils/win32-arggen.cc0000644000175000017500000000136012262345041020505 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "win32-arggen.hh" #include "MSXException.hh" #include "utf8_checked.hh" #include #include namespace openmsx { ArgumentGenerator::~ArgumentGenerator() { for (unsigned i = 0; i < argv.size(); ++i) { free(argv[i]); } } char** ArgumentGenerator::GetArguments(int& argc) { if (argv.empty()) { int cArgs; LPWSTR* pszArglist = CommandLineToArgvW(GetCommandLineW(), &cArgs); if (!pszArglist) { throw MSXException("Failed to obtain command line arguments"); } argv.resize(cArgs); for (int i = 0; i < cArgs; ++i) { argv[i] = strdup(utf8::utf16to8(pszArglist[i]).c_str()); } LocalFree(pszArglist); } argc = int(argv.size()); return argv.data(); } } // namespace openmsx #endif openmsx-0.10.0/src/utils/win32-dirent.hh0000644000175000017500000000376512262345041020554 0ustar manuelmanuel00000000000000#ifndef WIN32_DIRENT_HH #define WIN32_DIRENT_HH /* Copyright (C) 2001, 2006 Free Software Foundation, Inc. * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* Directory stream type. The miscellaneous Unix `readdir' implementations read directory data into a buffer and return `struct dirent *' pointers into it. */ // NB: Taken from http://www.koders.com/c/fid5F00BC983E0F005E15E8E590E461D363F6146CEB.aspx // Slightly reformatted/simplified to fit openMSX coding style. #ifdef _WIN32 #include #include // for INT_PTR #include namespace openmsx { struct dirstream { INT_PTR fd; // File descriptor. void* data; // Directory block. off_t filepos; // Position of next entry to read. std::wstring mask; // Initial file mask. }; struct dirent { long d_ino; off_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[256]; }; #define d_fileno d_ino // Backwards compatibility. // This is the data type of directory stream objects. // The actual structure is opaque to users. typedef struct dirstream DIR; DIR* opendir(const char* name); struct dirent* readdir(DIR* dir); int closedir(DIR* dir); void rewinddir(DIR* dir); void seekdir(DIR* dir, off_t offset); off_t telldir(DIR* dir); //int dirfd(DIR* dir); } // namespace openmsx #endif #endif // WIN32_DIRENT_HH openmsx-0.10.0/src/utils/MemoryOps.cc0000644000175000017500000001733612262345041020246 0ustar manuelmanuel00000000000000#include "MemoryOps.hh" #include "likely.hh" #include "build-info.hh" #include "systemfuncs.hh" #include "Math.hh" #include "unreachable.hh" #include #include #include #include // for std::bad_alloc #if ASM_X86 && defined _MSC_VER #include // for __stosd intrinsic #endif #ifdef __SSE2__ #include #endif namespace openmsx { namespace MemoryOps { #ifdef __SSE2__ #if ASM_X86_32 && defined _MSC_VER // Gcc has the _mm_set1_epi64x() function for both 32 and 64 bit. Visual studio // only has it for 64 bit. So we add it ourselves for vc++/32-bit. An // alternative would be to always use this routine, but this generates worse // code than the real _mm_set1_epi64x() function for gcc (both 32 and 64 bit). static inline __m128i _mm_set1_epi64x(uint64_t val) { uint32_t low = val >> 32; uint32_t high = val >> 0; return _mm_set_epi32(low, high, low, high); } #endif static inline void memset_64_SSE( uint64_t* dest, size_t num64, uint64_t val64) { if (unlikely(num64 == 0)) return; // Align at 16-byte boundary. if (unlikely(size_t(dest) & 8)) { dest[0] = val64; ++dest; --num64; } __m128i val128 = _mm_set1_epi64x(val64); uint64_t* e = dest + num64 - 3; for (/**/; dest < e; dest += 4) { _mm_store_si128(reinterpret_cast<__m128i*>(dest + 0), val128); _mm_store_si128(reinterpret_cast<__m128i*>(dest + 2), val128); } if (unlikely(num64 & 2)) { _mm_store_si128(reinterpret_cast<__m128i*>(dest), val128); dest += 2; } if (unlikely(num64 & 1)) { dest[0] = val64; } } #endif static inline void memset_64( uint64_t* dest, size_t num64, uint64_t val64) { assert((size_t(dest) % 8) == 0); // must be 8-byte aligned #ifdef __SSE2__ memset_64_SSE(dest, num64, val64); return; #endif uint64_t* e = dest + num64 - 3; for (/**/; dest < e; dest += 4) { dest[0] = val64; dest[1] = val64; dest[2] = val64; dest[3] = val64; } if (unlikely(num64 & 2)) { dest[0] = val64; dest[1] = val64; dest += 2; } if (unlikely(num64 & 1)) { dest[0] = val64; } } static inline void memset_32_2( uint32_t* dest, size_t num32, uint32_t val0, uint32_t val1) { assert((size_t(dest) % 4) == 0); // must be 4-byte aligned if (unlikely(num32 == 0)) return; // Align at 8-byte boundary. if (unlikely(size_t(dest) & 4)) { dest[0] = val1; // start at odd pixel ++dest; --num32; } uint64_t val64 = OPENMSX_BIGENDIAN ? (uint64_t(val0) << 32) | val1 : val0 | (uint64_t(val1) << 32); memset_64(reinterpret_cast(dest), num32 / 2, val64); if (unlikely(num32 & 1)) { dest[num32 - 1] = val0; } } static inline void memset_32(uint32_t* dest, size_t num32, uint32_t val32) { assert((size_t(dest) % 4) == 0); // must be 4-byte aligned #if ASM_X86 #if defined _MSC_VER // VC++'s __stosd intrinsic results in emulator benchmarks // running about 7% faster than with memset_32_2, streaming or not, // and about 3% faster than the C code below. __stosd(reinterpret_cast(dest), val32, num32); #else memset_32_2(dest, num32, val32, val32); #endif #elif defined __arm__ // Ideally the first mov(*) instruction could be omitted (and then // replace 'r3' with '%[val]'. But this can cause problems in the // 'stm' instructions when the compiler chooses a register // 'bigger' than r4 for [val]. See commit message for LOTS more // details. asm volatile ( "mov r3, %[val]\n\t" // (*) should not be needed "mov r4, r3\n\t" "mov r5, r3\n\t" "mov r6, r3\n\t" "subs %[num],%[num],#8\n\t" "bmi 1f\n" "mov r8, r3\n\t" "mov r9, r3\n\t" "mov r10,r3\n\t" "mov r12,r3\n\t" "0:\n\t" "stmia %[dest]!,{r3,r4,r5,r6,r8,r9,r10,r12}\n\t" "subs %[num],%[num],#8\n\t" "bpl 0b\n\t" "1:\n\t" "tst %[num],#4\n\t" "it ne\n\t" "stmne %[dest]!,{r3,r4,r5,r6}\n\t" "tst %[num],#2\n\t" "it ne\n\t" "stmne %[dest]!,{r3,r4}\n\t" "tst %[num],#1\n\t" "it ne\n\t" "strne r3,[%[dest]]\n\t" : [dest] "=r" (dest) , [num] "=r" (num32) : "[dest]" (dest) , "[num]" (num32) , [val] "r" (val32) : "memory" , "r3","r4","r5","r6","r8","r9","r10","r12" ); return; #else uint32_t* e = dest + num32 - 7; for (/**/; dest < e; dest += 8) { dest[0] = val32; dest[1] = val32; dest[2] = val32; dest[3] = val32; dest[4] = val32; dest[5] = val32; dest[6] = val32; dest[7] = val32; } if (unlikely(num32 & 4)) { dest[0] = val32; dest[1] = val32; dest[2] = val32; dest[3] = val32; dest += 4; } if (unlikely(num32 & 2)) { dest[0] = val32; dest[1] = val32; dest += 2; } if (unlikely(num32 & 1)) { dest[0] = val32; } #endif } static inline void memset_16_2( uint16_t* dest, size_t num16, uint16_t val0, uint16_t val1) { if (unlikely(num16 == 0)) return; // Align at 4-byte boundary. if (unlikely(size_t(dest) & 2)) { dest[0] = val1; // start at odd pixel ++dest; --num16; } uint32_t val32 = OPENMSX_BIGENDIAN ? (uint32_t(val0) << 16) | val1 : val0 | (uint32_t(val1) << 16); memset_32(reinterpret_cast(dest), num16 / 2, val32); if (unlikely(num16 & 1)) { dest[num16 - 1] = val0; } } static inline void memset_16(uint16_t* dest, size_t num16, uint16_t val16) { memset_16_2(dest, num16, val16, val16); } template void MemSet::operator()( Pixel* dest, size_t num, Pixel val) const { if (sizeof(Pixel) == 2) { memset_16(reinterpret_cast(dest), num, val); } else if (sizeof(Pixel) == 4) { memset_32(reinterpret_cast(dest), num, val); } else { UNREACHABLE; } } template void MemSet2::operator()( Pixel* dest, size_t num, Pixel val0, Pixel val1) const { if (sizeof(Pixel) == 2) { memset_16_2(reinterpret_cast(dest), num, val0, val1); } else if (sizeof(Pixel) == 4) { memset_32_2(reinterpret_cast(dest), num, val0, val1); } else { UNREACHABLE; } } // Force template instantiation template struct MemSet ; template struct MemSet ; template struct MemSet2; template struct MemSet2; /** Aligned memory (de)allocation */ // Helper class to keep track of aligned/unaligned pointer pairs class AllocMap { public: static AllocMap& instance() { static AllocMap oneInstance; return oneInstance; } void insert(void* aligned, void* unaligned) { assert(allocMap.find(aligned) == allocMap.end()); allocMap[aligned] = unaligned; } void* remove(void* aligned) { auto it = allocMap.find(aligned); assert(it != allocMap.end()); void* unaligned = it->second; allocMap.erase(it); return unaligned; } private: AllocMap() {} ~AllocMap() { assert(allocMap.empty()); } std::map allocMap; }; void* mallocAligned(size_t alignment, size_t size) { assert("must be a power of 2" && Math::isPowerOfTwo(alignment)); assert(alignment >= sizeof(void*)); #if HAVE_POSIX_MEMALIGN void* aligned; if (posix_memalign(&aligned, alignment, size)) { throw std::bad_alloc(); } #if defined DEBUG AllocMap::instance().insert(aligned, aligned); #endif return aligned; #elif defined _MSC_VER return _aligned_malloc(size, alignment); #else auto t = alignment - 1; void* unaligned = malloc(size + t); if (!unaligned) { throw std::bad_alloc(); } auto aligned = reinterpret_cast( (reinterpret_cast(unaligned) + t) & ~t); AllocMap::instance().insert(aligned, unaligned); return aligned; #endif } void freeAligned(void* aligned) { #if HAVE_POSIX_MEMALIGN #if defined DEBUG AllocMap::instance().remove(aligned); #endif free(aligned); #elif defined _MSC_VER return _aligned_free(aligned); #else void* unaligned = AllocMap::instance().remove(aligned); free(unaligned); #endif } } // namespace MemoryOps } // namespace openmsx openmsx-0.10.0/src/utils/DivModByConst.hh0000644000175000017500000003103712262345041021004 0ustar manuelmanuel00000000000000#ifndef DIVMODBYCONST #define DIVMODBYCONST #include "build-info.hh" #include "type_traits.hh" #include #include /** Utility class to optimize 64-bit divide/module by a 32-bit constant. * For 32-bit by 32-bit gcc already does this optimiztion (on 64-bit * CPUs gcc also does it for 64-bit operands). This optimization especially * helps on CPU without a HW division instruction (like ARM). * * Usage: * DivModByConst<123> dm; * uint32_t d = dm.div(x); // equivalent to d = x / 123; * uint32_t m = dm.mod(x); // equivalent to d = x % 123; */ namespace DivModByConstPrivate { template struct log2 : if_c, log2> {}; // Utility class to perform 128-bit by 128-bit division at compilation time template struct Div128_helper { static const uint64_t QL2 = (QL << 1); static const uint64_t QH2 = (QH << 1) + (QL2 < QL); static const uint64_t RL2 = (RL << 1) + (QH2 < QH); static const uint64_t RH2 = (RH << 1) + (RL2 < RL); static const bool C = (RH2 != DH) ? (RH2 < DH) : (RL2 < DL); static const uint64_t RL3 = C ? RL2 : RL2 - DL; static const uint64_t RH3 = C ? RH2 : RH2 - DH - (RL3 > RL2); static const uint64_t QL3 = C ? QL2 : QL2 + 1; static const uint64_t QH3 = C ? QH2 : ((QL3 != 0) ? QH2 : QH2 + 1); typedef Div128_helper Div; static const uint64_t quotientLow = Div::quotientLow; static const uint64_t quotientHigh = Div::quotientHigh; static const uint64_t remainderLow = Div::remainderLow; static const uint64_t remainderHigh = Div::remainderHigh; }; template struct Div128_helper { static const uint64_t quotientLow = QL; static const uint64_t quotientHigh = QH; static const uint64_t remainderLow = RL; static const uint64_t remainderHigh = RH; }; template struct Div128 : Div128_helper<0, 0, DividendH, DividendL, DividerH, DividerL, 128> {}; // equivalent to the following run-time loop: // while (!(M & 1)) { // M >>= 1; // --S; // } template struct DBCReduce { typedef DBCReduce R2; static const uint64_t M2 = R2::M2; static const uint32_t S2 = R2::S2; }; template struct DBCReduce { static const uint64_t M2 = M; static const uint32_t S2 = S; }; // equivalent to the following run-tim loop: // while (((m_low >> 1) < (m_high >> 1)) && (l > 0)) { // m_low >>= 1; // m_high >>= 1; // --l; // } template struct DBCReduce2Shift { static const uint64_t AH2 = AH / 2; static const uint64_t AL2 = AL / 2 + ((AH2 * 2 != AH) ? (1ull << 63) : 0); static const uint64_t BH2 = BH / 2; static const uint64_t BL2 = BL / 2 + ((BH2 * 2 != BH) ? (1ull << 63) : 0); }; template struct DBCReduce2Test { typedef DBCReduce2Shift S; static const bool C = (S::AH2 != S::BH2) ? (S::AH2 < S::BH2) : (S::AL2 < S::BL2); static const bool value = C && (L > 0); }; template struct DBCReduce2Loop { typedef DBCReduce2Shift S; typedef DBCReduce2Test T; typedef DBCReduce2Loop R; static const uint64_t MLH = R::MLH; static const uint64_t MLL = R::MLL; static const uint64_t MHH = R::MHH; static const uint64_t MHL = R::MHL; static const uint32_t L = R::L; }; template struct DBCReduce2Loop { static const uint64_t MLH = AH; static const uint64_t MLL = AL; static const uint64_t MHH = BH; static const uint64_t MHL = BL; static const uint32_t L = LL; }; template struct DBCReduce2 { typedef DBCReduce2Test T; typedef DBCReduce2Loop R; static const uint64_t MLH = R::MLH; static const uint64_t MLL = R::MLL; static const uint64_t MHH = R::MHH; static const uint64_t MHL = R::MHL; static const uint32_t L = R::L; }; template struct DBCAlgo1 { // division possible by only shifting uint32_t operator()(uint64_t dividend) const { return dividend >> S; } }; static inline uint64_t mla64(uint64_t a, uint64_t b, uint64_t c) { // equivalent to this: // return (__uint128_t(a) * b + c) >> 64; uint64_t t1 = uint64_t(uint32_t(a)) * uint32_t(b); uint64_t t2 = (a >> 32) * uint32_t(b); uint64_t t3 = uint32_t(a) * (b >> 32); uint64_t t4 = (a >> 32) * (b >> 32); uint64_t s1 = uint64_t(uint32_t(c)) + uint32_t(t1); uint64_t s2 = (s1 >> 32) + (c >> 32) + (t1 >> 32) + t2; uint64_t s3 = uint64_t(uint32_t(s2)) + uint32_t(t3); uint64_t s4 = (s3 >> 32) + (s2 >> 32) + (t3 >> 32) + t4; return s4; } template struct DBCAlgo2 { // division possible by multiplication and shift uint32_t operator()(uint64_t dividend) const { typedef DBCReduce R; #if ASM_X86_32 || defined(__arm__) const uint32_t _ah_ = R::M2 >> 32; const uint32_t _al_ = uint32_t((R::M2 << 32) >> 32); // Suppress VC++ C4310 warning const uint32_t _bh_ = dividend >> 32; const uint32_t _bl_ = uint32_t(dividend); #endif #if ASM_X86_32 #ifdef _MSC_VER uint32_t _tl_; register uint32_t result; __asm { // It's worth noting that simple benchmarks show this to be // no faster than straight division on an Intel E8400 // // eax and edx are used with mul // ecx = bl // esi = ah mov ecx,_bl_ mov esi,_ah_ // ebx is th mov eax,esi mul ecx mov _tl_,eax mov ebx,edx mov eax,_al_ mul ecx add _tl_,edx adc ebx,0 // ecx = bh now // edi is cl mov ecx,_bh_ mov eax,esi mul ecx mov edi,eax // esi is ch now mov esi,edx mov eax,_al_ mul ecx add _tl_,eax adc ebx,edx adc esi,0 add edi,ebx adc esi,0 // Sadly, no way to make this an immediate in VC++ mov cl,byte ptr [R::S2] shrd edi,esi,cl mov result,edi } #ifdef DEBUG uint32_t realResult = uint32_t(mla64(dividend, R::M2, 0) >> R::S2); assert(realResult == result); #endif return result; #else uint32_t th, tl, ch, cl; asm ( "movl %[AH],%%eax\n\t" "mull %[BL]\n\t" "movl %%eax,%[TL]\n\t" "movl %%edx,%[TH]\n\t" "movl %[AL],%%eax\n\t" "mull %[BL]\n\t" "addl %%edx,%[TL]\n\t" "adcl $0,%[TH]\n\t" "movl %[AH],%%eax\n\t" "mull %[BH]\n\t" "movl %%eax,%[CL]\n\t" "movl %%edx,%[CH]\n\t" "movl %[AL],%%eax\n\t" "mull %[BH]\n\t" "addl %%eax,%[TL]\n\t" "adcl %%edx,%[TH]\n\t" "adcl $0,%[CH]\n\t" "addl %[TH],%[CL]\n\t" "adcl $0,%[CH]\n\t" : [CH] "=&rm" (ch) , [TH] "=&r" (th) , [CL] "=rm" (cl) , [TL] "=&rm" (tl) : [AH] "g" (_ah_) , [AL] "g" (_al_) , [BH] "rm" (_bh_) , [BL] "[CL]" (_bl_) : "eax","edx" ); asm ( "shrd %[SH],%[CH],%[CL]\n\t" : [CL] "=rm" (cl) : [CH] "r" (ch) , "[CL]" (cl) , [SH] "i" (R::S2) ); return cl; #endif #elif defined(__arm__) uint32_t res; uint32_t th,tl; asm volatile ( "umull %[TH],%[TL],%[AL],%[BL]\n\t" "eors %[TH],%[TH]\n\t" "umlal %[TL],%[TH],%[AH],%[BL]\n\t" "umull %[BL],%[AL],%[BH],%[AL]\n\t" "adds %[TL],%[TL],%[BL]\n\t" "adcs %[TH],%[TH],%[AL]\n\t" "mov %[TL],#0\n\t" "adc %[TL],%[TL],%[TL]\n\t" "umlal %[TH],%[TL],%[AH],%[BH]\n\t" "lsr %[RES],%[TH],%[S]\n\t" //"orr %[RES],%[RES],%[TL],LSL %[S32]\n\t" // not thumb2 "lsls %[TL],%[TL],%[S32]\n\t" "orrs %[RES],%[RES],%[TL]\n\t" : [RES] "=r" (res) , [TH] "=&r" (th) , [TL] "=&r" (tl) : [AH] "r" (_ah_) , [AL] "r" (_al_) , [BH] "r" (_bh_) , [BL] "[RES]" (_bl_) , [S] "M" (R::S2) , [S32] "M" (32 - R::S2) ); return res; #else uint64_t h = mla64(dividend, R::M2, 0); uint64_t result = h >> R::S2; #ifdef DEBUG // we don't even want this overhead in devel builds assert(result == uint32_t(result)); #endif return uint32_t(result); #endif } }; template struct DBCAlgo3 { // division possible by multiplication, addition and shift static const uint32_t S = log2::value - 1; typedef Div128<1 << S, 0, 0, DIVISOR> D; static const uint64_t M = D::quotientLow + (D::remainderLow > (DIVISOR / 2)); uint32_t operator()(uint64_t dividend) const { typedef DBCReduce R; #if ASM_X86_32 || defined(__arm__) const uint32_t ah = R::M2 >> 32; const uint32_t al = uint32_t(R::M2); const uint32_t bh = dividend >> 32; const uint32_t bl = dividend; #endif #if ASM_X86_32 uint32_t th, tl, ch, cl; asm ( "mov %[AH],%%eax\n\t" "mull %[BL]\n\t" "mov %%eax,%[TL]\n\t" "mov %%edx,%[TH]\n\t" "mov %[AL],%%eax\n\t" "mull %[BL]\n\t" "add %[AL],%%eax\n\t" "adc %[AH],%%edx\n\t" "adc $0,%[TH]\n\t" "add %%edx,%[TL]\n\t" "adc $0,%[TH]\n\t" "mov %[AH],%%eax\n\t" "mull %[BH]\n\t" "mov %%eax,%[CL]\n\t" "mov %%edx,%[CH]\n\t" "mov %[AL],%%eax\n\t" "mull %[BH]\n\t" "add %%eax,%[TL]\n\t" "adc %%edx,%[TH]\n\t" "adc $0,%[CH]\n\t" "add %[TH],%[CL]\n\t" "adc $0,%[CH]\n\t" : [CH] "=&rm" (ch) , [TH] "=&r" (th) , [CL] "=rm" (cl) , [TL] "=&rm" (tl) : [AH] "g" (ah) , [AL] "g" (al) , [BH] "rm" (bh) , [BL] "[CL]" (bl) : "eax","edx" ); asm ( "shrd %[SH],%[CH],%[CL]\n\t" : [CL] "=rm" (cl) : [CH] "r" (ch) , "[CL]" (cl) , [SH] "i" (R::S2) ); return cl; #elif defined(__arm__) uint32_t res; uint32_t th,tl; asm volatile ( "umull %[TH],%[TL],%[AL],%[BL]\n\t" "adds %[TH],%[TH],%[AL]\n\t" "adcs %[TL],%[TL],%[AH]\n\t" "mov %[TH],#0\n\t" "adc %[TH],%[TH],%[TH]\n\t" "umlal %[TL],%[TH],%[AH],%[BL]\n\t" "umull %[BL],%[AL],%[BH],%[AL]\n\t" "adds %[TL],%[TL],%[BL]\n\t" "adcs %[TH],%[TH],%[AL]\n\t" "mov %[TL],#0\n\t" "adc %[TL],%[TL],%[TL]\n\t" "umlal %[TH],%[TL],%[AH],%[BH]\n\t" "lsr %[RES],%[TH],%[S]\n\t" //"orr %[RES],%[RES],%[TL],LSL %[S32]\n\t" // not thumb2 "lsls %[TL],%[TL],%[S32]\n\t" "orrs %[RES],%[RES],%[TL]\n\t" : [RES] "=r" (res) , [TH] "=&r" (th) , [TL] "=&r" (tl) : [AH] "r" (ah) , [AL] "r" (al) , [BH] "r" (bh) , [BL] "[RES]" (bl) , [S] "M" (R::S2) , [S32] "M" (32 - R::S2) ); return res; #else uint64_t h = mla64(dividend, R::M2, R::M2); return h >> R::S2; #endif } }; template struct DBCHelper3 : if_c , DBCAlgo3> {}; template struct DBCHelper2 { static const uint32_t L = log2::value; static const uint64_t J = 0xffffffffffffffffull % DIVISOR; typedef Div128<1 << L, 0, 0, 0xffffffffffffffffull - J> K; typedef Div128< 1 << L, 0, 0, DIVISOR> M_LOW; typedef Div128<(1 << L) + K::quotientHigh, K::quotientLow, 0, DIVISOR> M_HIGH; typedef DBCReduce2 R; uint32_t operator()(uint64_t dividend) const { DBCHelper3 dbc; return dbc(dividend); } }; template struct DBCHelper1 : if_c, if_c , DBCHelper1>> {}; } // namespace DivModByConstPrivate template struct DivModByConst { uint32_t div(uint64_t dividend) const { #ifdef __x86_64 // on 64-bit CPUs gcc already does this // optimization (and better) return uint32_t(dividend / DIVISOR); #else DivModByConstPrivate::DBCHelper1 dbc; return dbc(dividend); #endif } uint32_t mod(uint64_t dividend) const { uint64_t result; #ifdef __x86_64 result = dividend % DIVISOR; #else result = dividend - DIVISOR * div(dividend); #endif #ifdef DEBUG // we don't even want this overhead in devel builds assert(result == uint32_t(result)); #endif return uint32_t(result); } }; #endif // DIVMODBYCONST openmsx-0.10.0/src/utils/Math.hh0000644000175000017500000001476112262345041017216 0ustar manuelmanuel00000000000000#ifndef MATH_HH #define MATH_HH #include "openmsx.hh" #include "inline.hh" #include "likely.hh" #include "build-info.hh" #include #include namespace openmsx { #ifdef _MSC_VER // C99 math functions missing from VC++'s CRT as of 2008 // TODO - define HAVE_C99MATHOPS instead long lrint(double x); long lrintf(float x); float truncf(float x); double round(double x); #endif namespace Math { /** Is the given number an integer power of 2? * Not correct for zero (according to this test 0 is a power of 2). */ inline bool isPowerOfTwo(unsigned a) { return (a & (a - 1)) == 0; } /** Returns the smallest number that is both >=a and a power of two. */ unsigned powerOfTwo(unsigned a); /** Returns two gaussian distributed random numbers. * We return two numbers instead of one because the second number comes for * free in the current implementation. */ void gaussian2(double& r1, double& r2); /** Clips x to the range [LO,HI]. * Slightly faster than std::min(HI, std::max(LO, x)) * especially when no clipping is required. */ template inline int clip(int x) { return unsigned(x - LO) <= unsigned(HI - LO) ? x : (x < HI ? LO : HI); } /** Clip x to range [-32768,32767]. Special case of the version above. * Optimized for the case when no clipping is needed. */ inline short clipIntToShort(int x) { static_assert((-1 >> 1) == -1, "right-shift must preserve sign"); return likely(short(x) == x) ? x : (0x7FFF - (x >> 31)); } /** Clip x to range [0,255]. * Optimized for the case when no clipping is needed. */ inline byte clipIntToByte(int x) { static_assert((-1 >> 1) == -1, "right-shift must preserve sign"); return likely(byte(x) == x) ? x : ~(x >> 31); } /** Clips r * factor to the range [LO,HI]. */ template inline int clip(double r, double factor) { int a = int(round(r * factor)); return std::min(std::max(a, LO), HI); } /** Calculate greatest common divider of two strictly positive integers. * Classical implementation is like this: * while (unsigned t = b % a) { b = a; a = t; } * return a; * The following implementation avoids the costly modulo operation. It * is about 40% faster on my machine. * * require: a != 0 && b != 0 */ inline unsigned gcd(unsigned a, unsigned b) { unsigned k = 0; while (((a & 1) == 0) && ((b & 1) == 0)) { a >>= 1; b >>= 1; ++k; } // either a or b (or both) is odd while ((a & 1) == 0) a >>= 1; while ((b & 1) == 0) b >>= 1; // both a and b odd while (a != b) { if (a >= b) { a -= b; do { a >>= 1; } while ((a & 1) == 0); } else { b -= a; do { b >>= 1; } while ((b & 1) == 0); } } return b << k; } /** Reverse the lower N bits of a given value. * The upper 32-N bits from the input are ignored and will be returned as 0. * For example reverseNBits('xxxabcde', 5) returns '000edcba' (binary notation). */ inline unsigned reverseNBits(unsigned x, unsigned bits) { unsigned ret = 0; while (bits--) { ret = (ret << 1) | (x & 1); x >>= 1; } return ret; /* Just for fun I tried the asm version below (the carry-flag trick * cannot be described in plain C). It's correct and generates shorter * code (both less instructions and less bytes). But it doesn't * actually run faster on the machine I tested on, or only a tiny bit * (possibly because of dependency chains and processor stalls???). * However a big disadvantage of this asm version is that when called * with compile-time constant arguments, this version performs exactly * the same, while the version above can be further optimized by the * compiler (constant-propagation, loop unrolling, ...). unsigned ret = 0; if (bits) { asm ( "1: shr %[VAL]\n" " adc %[RET],%[RET]\n" " dec %[BITS]\n" " jne 1b\n" : [VAL] "+r" (val) , [BITS] "+r" (bits) , [RET] "+r" (ret) ); } return ret; */ /* Maarten suggested the following approach with O(lg(N)) time * complexity (the version above is O(N)). * - reverse full (32-bit) word: O(lg(N)) * - shift right over 32-N bits: O(1) * Note: In some lower end CPU the shift-over-N-bits instruction itself * is O(N), in that case this whole algorithm is O(N) * Note2: Instead of '32' it's also possible to use a lower power of 2, * as long as it's bigger than or equal to N. * This algorithm may or may not be faster than the version above, I * didn't try it yet. Also because this routine is _NOT_ performance * critical _AT_ALL_ currently. */ } /** Reverse the bits in a byte. * This is equivalent to (but faster than) reverseNBits(x, 8); */ inline byte reverseByte(byte a) { // Classical implementation (can be extended to 16 and 32 bits) // a = ((a & 0xF0) >> 4) | ((a & 0x0F) << 4); // a = ((a & 0xCC) >> 2) | ((a & 0x33) << 2); // a = ((a & 0xAA) >> 1) | ((a & 0x55) << 1); // return a; // The versions below are specific to reverse a single byte (can't // easily be extended to wider types). Found these tricks on: // http://graphics.stanford.edu/~seander/bithacks.html #ifdef __x86_64 // on 64-bit systems this is slightly faster return (((a * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL) >> 32; #else // on 32-bit systems this is faster return (((a * 0x0802 & 0x22110) | (a * 0x8020 & 0x88440)) * 0x10101) >> 16; #endif } /** Returns the smallest number of the form 2^n-1 that is greater or equal * to the given number. * The resulting number has the same number of leading zeros as the input, * but starting from the first 1-bit in the input all bits more to the right * are also 1. */ template inline T floodRight(T x) { x |= x >> 1; x |= x >> 2; x |= x >> 4; x |= x >> ((sizeof(x) >= 2) ? 8 : 0); // Written in a weird way to x |= x >> ((sizeof(x) >= 4) ? 16 : 0); // suppress compiler warnings. x |= x >> ((sizeof(x) >= 8) ? 32 : 0); // Generates equally efficient return x; // code. } /** Count the number of leading zero-bits in the given word. * The result is undefined when the input is zero (all bits are zero). */ inline unsigned countLeadingZeros(unsigned x) { #ifdef __GNUC__ // actually this only exists starting from gcc-3.4.x return __builtin_clz(x); // undefined when x==0 #else // gives incorrect result for x==0, but that doesn't matter here unsigned lz = 0; if (x <= 0x0000ffff) { lz += 16; x <<= 16; } if (x <= 0x00ffffff) { lz += 8; x <<= 8; } if (x <= 0x0fffffff) { lz += 4; x <<= 4; } lz += (0x55ac >> ((x >> 27) & 0x1e)) & 0x3; return lz; #endif } } // namespace Math } // namespace openmsx #endif // MATH_HH openmsx-0.10.0/src/utils/CircularBuffer.cc0000644000175000017500000000146212262345041021203 0ustar manuelmanuel00000000000000#if 0 #include "CircularBuffer.hh" #include int main() { openmsx::CircularBuffer buf; assert(buf.isEmpty()); assert(!buf.isFull()); assert(buf.size() == 0); buf.addBack(15); assert(!buf.isEmpty()); assert(!buf.isFull()); assert(buf.size() == 1); assert(buf[0] == 15); buf[0] = 25; assert(buf[0] == 25); buf.addFront(17); assert(!buf.isEmpty()); assert(buf.isFull()); assert(buf.size() == 2); assert(buf[0] == 17); assert(buf[1] == 25); buf[1] = 35; assert(buf[0] == 17); assert(buf[1] == 35); buf[0] = 27; assert(buf[0] == 27); assert(buf[1] == 35); int a = buf.removeBack(); assert(a == 35); assert(buf.size() == 1); assert(buf[0] == 27); int b = buf.removeFront(); assert(b == 27); assert(buf.isEmpty()); std::cout << "Test passed!" << std::endl; } #endif openmsx-0.10.0/src/utils/Subject.hh0000644000175000017500000000270212262345041017714 0ustar manuelmanuel00000000000000#ifndef SUBJECT_HH #define SUBJECT_HH #include "Observer.hh" #include "ScopedAssign.hh" #include #include #include namespace openmsx { /** * Generic Gang-of-Four Subject class of the Observer pattern, templatized * edition. */ template class Subject { public: void attach(Observer& observer); void detach(Observer& observer); protected: Subject(); ~Subject(); void notify() const; private: std::vector*> observers; #ifndef NDEBUG mutable bool notifyInProgress; #endif }; template Subject::Subject() #ifndef NDEBUG : notifyInProgress(false) #endif { } template Subject::~Subject() { assert(!notifyInProgress); auto copy = observers; for (auto& o : copy) { o->subjectDeleted(*static_cast(this)); } assert(observers.empty()); } template void Subject::attach(Observer& observer) { assert(!notifyInProgress); observers.push_back(&observer); } template void Subject::detach(Observer& observer) { assert(!notifyInProgress); auto it = find(observers.begin(), observers.end(), &observer); assert(it != observers.end()); observers.erase(it); } template void Subject::notify() const { #ifndef NDEBUG assert(!notifyInProgress); ScopedAssign sa(notifyInProgress, true); #endif for (auto& o : observers) { o->update(*static_cast(this)); } } } // namespace openmsx #endif openmsx-0.10.0/src/utils/noncopyable.hh0000644000175000017500000000074612262345041020634 0ustar manuelmanuel00000000000000#ifndef NONCOPYABLE_HH #define NONCOPYABLE_HH /** * Based on boost::noncopyable, see boost documentation: * http://www.boost.org/libs/utility * * Summary: * Class noncopyable is a base class. Derive your own class from noncopyable * when you want to prohibit copy construction and copy assignment. */ class noncopyable { protected: noncopyable() {} ~noncopyable() {} private: noncopyable(const noncopyable&); const noncopyable& operator=(const noncopyable&); }; #endif openmsx-0.10.0/src/utils/stringsp.hh0000644000175000017500000000026512262345041020170 0ustar manuelmanuel00000000000000#ifndef STRINGSP_HH #define STRINGSP_HH #ifndef _MSC_VER #include #else #include #define strcasecmp _stricmp #define strncasecmp _strnicmp #endif #endif openmsx-0.10.0/src/utils/type_traits.hh0000644000175000017500000000044012262345041020661 0ustar manuelmanuel00000000000000#ifndef TYPE_TRAITS_HH #define TYPE_TRAITS_HH // if_ template struct if_c : F {}; template< class T, class F> struct if_c : T {}; template struct if_ : if_c {}; #endif openmsx-0.10.0/src/utils/utf8_unchecked.hh0000644000175000017500000001451412262345041021220 0ustar manuelmanuel00000000000000// UTF8-CPP http://utfcpp.sourceforge.net/ // Slightly simplified (and reformatted) to fit openMSX coding style. // Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_UNCHECKED_HH #define UTF8_UNCHECKED_HH #include "utf8_core.hh" #include "string_ref.hh" namespace utf8 { namespace unchecked { template octet_iterator append(uint32_t cp, octet_iterator result) { if (cp < 0x80) { // one octet *result++ = cp; } else if (cp < 0x800) { // two octets *result++ = ((cp >> 6) ) | 0xc0; *result++ = ((cp >> 0) & 0x3f) | 0x80; } else if (cp < 0x10000) { // three octets *result++ = ((cp >> 12) ) | 0xe0; *result++ = ((cp >> 6) & 0x3f) | 0x80; *result++ = ((cp >> 0) & 0x3f) | 0x80; } else { // four octets *result++ = ((cp >> 18) ) | 0xf0; *result++ = ((cp >> 12) & 0x3f) | 0x80; *result++ = ((cp >> 6) & 0x3f) | 0x80; *result++ = ((cp >> 0) & 0x3f) | 0x80; } return result; } template uint32_t next(octet_iterator& it) { uint32_t cp = *it; switch (utf8::internal::sequence_length(cp)) { case 1: break; case 2: ++it; cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); break; case 3: ++it; cp = ((cp << 12) & 0xffff) + ((*it << 6) & 0xfff); ++it; cp += (*it) & 0x3f; break; case 4: ++it; cp = ((cp << 18) & 0x1fffff) + ((*it << 12) & 0x3ffff); ++it; cp += (*it << 6) & 0xfff; ++it; cp += (*it) & 0x3f; break; } ++it; return cp; } template uint32_t peek_next(octet_iterator it) { return next(it); } template uint32_t prior(octet_iterator& it) { while (internal::is_trail(*(--it))) ; auto temp = it; return next(temp); } template void advance(octet_iterator& it, distance_type n) { for (distance_type i = 0; i < n; ++i) { unchecked::next(it); } } template typename std::iterator_traits::difference_type distance(octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) { unchecked::next(first); } return dist; } template octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = *start++; // Take care of surrogate pairs first if (internal::is_surrogate(cp)) { uint32_t trail_surrogate = *start++; cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; } result = append(cp, result); } return result; } template u16bit_iterator utf8to16(octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start != end) { uint32_t cp = next(start); if (cp > 0xffff) { // make a surrogate pair *result++ = (cp >> 10) + internal::LEAD_OFFSET; *result++ = (cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN; } else { *result++ = cp; } } return result; } template octet_iterator utf32to8(u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) { result = append(*start++, result); } return result; } template u32bit_iterator utf8to32(octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) { *result++ = next(start); } return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; public: iterator() {}; explicit iterator(const octet_iterator& octet_it) : it(octet_it) {} // the default "big three" are OK octet_iterator base() const { return it; } uint32_t operator*() const { octet_iterator temp = it; return next(temp); } bool operator==(const iterator& rhs) const { return it == rhs.it; } bool operator!=(const iterator& rhs) const { return !(operator==(rhs)); } iterator& operator++() { std::advance(it, internal::sequence_length(*it)); return *this; } iterator operator++(int) { auto temp = *this; std::advance(it, internal::sequence_length(*it)); return temp; } iterator& operator--() { prior(it); return *this; } iterator operator--(int) { auto temp = *this; prior(it); return temp; } }; // convenience functions inline size_t size(string_ref utf8) { return utf8::unchecked::distance(utf8.begin(), utf8.end()); } inline string_ref substr(string_ref utf8, string_ref::size_type first = 0, string_ref::size_type len = string_ref::npos) { auto begin = utf8.begin(); utf8::unchecked::advance(begin, first); string_ref::const_iterator end; if (len != string_ref::npos) { end = begin; while (len && (end != utf8.end())) { unchecked::next(end); --len; } } else { end = utf8.end(); } return string_ref(begin, end); } } // namespace unchecked } // namespace utf8 #endif openmsx-0.10.0/src/utils/Base64.cc0000644000175000017500000000563612262345041017340 0ustar manuelmanuel00000000000000#include "Base64.hh" #include "xrange.hh" #include #include namespace Base64 { using std::string; typedef unsigned char byte; static inline char encode(byte c) { static const char* const base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; assert(c < 64); return base64_chars[c]; } static inline byte decode(unsigned char c) { if ('A' <= c && c <= 'Z') { return c - 'A'; } else if ('a' <= c && c <= 'z') { return c - 'a' + 26; } else if ('0' <= c && c <= '9') { return c - '0' + 52; } else if (c == '+') { return 62; } else if (c == '/') { return 63; } else { return byte(-1); } } string encode(const void* input_, size_t inSize) { auto input = static_cast(input_); auto outSize = ((inSize + 44) / 45) * 61; // overestimation string ret(outSize, 0); // too big size_t out = 0; while (inSize) { if (!ret.empty()) ret += '\n'; auto n2 = std::min(45, inSize); auto n = unsigned(n2); for (/**/; n >= 3; n -= 3) { ret[out++] = encode( (input[0] & 0xfc) >> 2); ret[out++] = encode(((input[0] & 0x03) << 4) + ((input[1] & 0xf0) >> 4)); ret[out++] = encode(((input[1] & 0x0f) << 2) + ((input[2] & 0xc0) >> 6)); ret[out++] = encode( (input[2] & 0x3f) >> 0); input += 3; } if (n) { byte buf3[3] = { 0, 0, 0 }; for (unsigned i = 0; i < n; ++i) { buf3[i] = input[i]; } byte buf4[4]; buf4[0] = (buf3[0] & 0xfc) >> 2; buf4[1] = ((buf3[0] & 0x03) << 4) + ((buf3[1] & 0xf0) >> 4); buf4[2] = ((buf3[1] & 0x0f) << 2) + ((buf3[2] & 0xc0) >> 6); buf4[3] = (buf3[2] & 0x3f) >> 0; for (unsigned j = 0; (j < n + 1); ++j) { ret[out++] = encode(buf4[j]); } for (/**/; n < 3; ++n) { ret[out++] = '='; } } inSize -= n2; } assert(outSize >= out); ret.resize(out); // shrink to correct size return ret; } string decode(const string& input) { auto inSize = input.size(); auto outSize = (inSize * 3 + 3) / 4; // overestimation string ret(outSize, 0); // too big unsigned i = 0; size_t out = 0; byte buf4[4]; for (auto in : xrange(inSize)) { byte d = decode(input[in]); if (d == byte(-1)) continue; buf4[i++] = d; if (i == 4) { i = 0; ret[out++] = char(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4)); ret[out++] = char(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2)); ret[out++] = char(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0)); } } if (i) { for (unsigned j = i; j < 4; ++j) { buf4[j] = 0; } byte buf3[3]; buf3[0] = ((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4); buf3[1] = ((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2); buf3[2] = ((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0); for (unsigned j = 0; (j < i - 1); ++j) { ret[out++] = buf3[j]; } } assert(outSize >= out); ret.resize(out); // shrink to correct size return ret; } } // namespace Base64 openmsx-0.10.0/src/utils/win32-dirent.cc0000644000175000017500000000646012262345041020535 0ustar manuelmanuel00000000000000/* Copyright (C) 2001, 2006 Free Software Foundation, Inc. * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // NB: Taken from http://www.koders.com/c/fidB38A2E97F60B8A0C903631F8C60078DE8C6C8433.aspx // Also modified to use Unicode calls explicitly // Slightly reformatted/simplified to fit openMSX coding style. #ifdef _WIN32 #include "win32-dirent.hh" #include "utf8_checked.hh" #include "MSXException.hh" #include "StringOp.hh" #include "countof.hh" #include #include #include #include namespace openmsx { DIR* opendir(const char* name) { if (!name || !*name) return nullptr; std::wstring nameW = utf8::utf8to16(name); if (!StringOp::endsWith(name, '/') && !StringOp::endsWith(name, "\\")) { nameW += L"\\*"; } else { nameW += L"*"; } HANDLE hnd; WIN32_FIND_DATAW find; if ((hnd = FindFirstFileW(nameW.c_str(), &find)) == INVALID_HANDLE_VALUE) { return nullptr; } DIR* dir = new DIR; dir->mask = nameW; dir->fd = reinterpret_cast(hnd); dir->data = new WIN32_FIND_DATAW; dir->filepos = 0; memcpy(dir->data, &find, sizeof(find)); return dir; } dirent* readdir(DIR* dir) { static dirent entry; entry.d_ino = 0; entry.d_type = 0; auto find = static_cast(dir->data); if (dir->filepos) { if (!FindNextFileW(reinterpret_cast(dir->fd), find)) { return nullptr; } } std::string d_name = utf8::utf16to8(find->cFileName); strncpy(entry.d_name, d_name.c_str(), countof(entry.d_name)); entry.d_off = dir->filepos; entry.d_reclen = static_cast(strlen(entry.d_name)); dir->filepos++; return &entry; } int closedir(DIR* dir) { auto hnd = reinterpret_cast(dir->fd); delete static_cast(dir->data); delete dir; return FindClose(hnd) ? 0 : -1; } void rewinddir(DIR* dir) { auto hnd = reinterpret_cast(dir->fd); auto find = static_cast(dir->data); FindClose(hnd); hnd = FindFirstFileW(dir->mask.c_str(), find); dir->fd = reinterpret_cast(hnd); dir->filepos = 0; } void seekdir(DIR* dir, off_t offset) { rewinddir(dir); for (off_t n = 0; n < offset; ++n) { if (FindNextFileW(reinterpret_cast(dir->fd), static_cast(dir->data))) { dir->filepos++; } } } off_t telldir(DIR* dir) { return dir->filepos; } // For correctness on 64-bit Windows, this function would need to maintain an // internal map of ints to HANDLEs, since HANDLEs are sizeof(void*). It's not // used at the moment in the openMSX sources, so let's not bother for now. /*int dirfd(DIR* dir) { return dir->fd; }*/ } // namespace openmsx #endif openmsx-0.10.0/src/utils/rapidsax.hh0000644000175000017500000004520712262345041020137 0ustar manuelmanuel00000000000000#ifndef RAPIDSAX_HH #define RAPIDSAX_HH // This code is _heavily_ based on RapidXml 1.13 // http://rapidxml.sourceforge.net/ // // RapidXml is a very fast XML parser. // http://xmlbench.sourceforge.net/results/benchmark200910/index.html // One of the main reasons it can be this fast is that doesn't do any string // copies. Instead the XML input data is modified in-place (e.g. for stuff like // < replacements). Though this also means the output produced by the parser // is tied to the lifetime of the XML input data. // // RapidXml produces a DOM-like output. This parser has a SAX-like interface. #include "string_ref.hh" namespace rapidsax { // Parse given XML text and call callback functions in the given handler. // - XML text must be zero-terminated // - Handler must implement the methods defined in NullHandler (below). An // easy way to do this is to inherit from NullHandler and only reimplement // the methods that you need. // - The behavior of the parser can be fine-tuned with the FLAGS parameter, // see below for more details. // - When a parse error is encounter, an instance of ParseError is thrown. // - The lifetime of the string_ref's in the callback handler is the same as // the lifetime of the input XML data (no string copies are made, instead // the XML file is modified in-place and references to this data are passed). template void parse(HANDLER& handler, char* xml); // Flags that influence parsing behavior. The flags can be OR'ed together. // Should XML entities like < be expanded or not? static const int noEntityTranslation = 0x1; // Should leading and trailing whitespace be trimmed? static const int trimWhitespace = 0x2; // Should sequences of whitespace characters be replaced with a single // space character? static const int normalizeWhitespace = 0x4; // Callback handler with all empty implementations (can be used as a base // class in case you only need to reimplement a few of the methods). class NullHandler { public: // Called when an opening XML tag is encountered. // 'name' is the name of the XML tag. void start(string_ref /*name*/) {} // Called when a XML tag is closed. // Note: the parser does currently not check whether the name of the // opening nd closing tags matches. void stop() {} // Called when text inside a tag is parsed. // XML entities are replaced (optional) // Whitespace is (optionally) trimmed or normalized. // This method is not called for an empty text string. // (Unlike other SAX parsers) the whole text string is always // passed in a single chunk (so no need to concatenate this text // with previous chunks in the callback). void text(string_ref /*text*/) {} // Called for each parsed attribute. // Attributes can occur inside xml tags or inside XML declarations. void attribute(string_ref /*name*/, string_ref /*value*/) {} // Called for parsed CDATA sections. void cdata(string_ref /*value*/) {} // Called when a XML comment () is parsed. void comment(string_ref /*value*/) {} // Called when XML declaration () is parsed. // Inside a XML declaration there can be attributes. void declarationStart() {} void declarationStop() {} // Called when the is parsed. void doctype(string_ref /*text*/) {} // Called when XML processing instructions () are parsed. void procInstr(string_ref /*target*/, string_ref /*instr*/) {} }; class ParseError { public: ParseError(const char* what, char* where) : m_what(what) , m_where(where) { } const char* what() const { return m_what; } char* where() const { return m_where; } private: const char* m_what; char* m_where; }; namespace internal { typedef unsigned char u8; extern const bool lutWhitespace[256]; // Whitespace table extern const bool lutNodeName[256]; // Node name table extern const bool lutText[256]; // Text table extern const bool lutTextPureNoWs[256]; // Text table extern const bool lutTextPureWithWs[256]; // Text table extern const bool lutAttributeName[256]; // Attribute name table extern const bool lutAttributeData1[256]; // Attribute data table, single quote extern const bool lutAttributeData1Pure[256]; // Attribute data table, single quote extern const bool lutAttributeData2[256]; // Attribute data table, double quotes extern const bool lutAttributeData2Pure[256]; // Attribute data table, double quotes extern const u8 lutDigits[256]; // Digits // Detect whitespace character struct WhitespacePred { static bool test(char ch) { return lutWhitespace[u8(ch)]; } }; // Detect node name character struct NodeNamePred { static bool test(char ch) { return lutNodeName[u8(ch)]; } }; // Detect attribute name character struct AttributeNamePred { static bool test(char ch) { return lutAttributeName[u8(ch)]; } }; // Detect text character (PCDATA) struct TextPred { static bool test(char ch) { return lutText[u8(ch)]; } }; // Detect text character (PCDATA) that does not require processing struct TextPureNoWsPred { static bool test(char ch) { return lutTextPureNoWs[u8(ch)]; } }; // Detect text character (PCDATA) that does not require processing struct TextPureWithWsPred { static bool test(char ch) { return lutTextPureWithWs[u8(ch)]; } }; // Detect attribute value character (single quote) struct AttPred1 { static bool test(char ch) { return lutAttributeData1[u8(ch)]; } }; // Detect attribute value character (double quote) struct AttPred2 { static bool test(char ch) { return lutAttributeData2[u8(ch)]; } }; // Detect attribute value character (single quote) struct AttPurePred1 { static bool test(char ch) { return lutAttributeData1Pure[u8(ch)]; } }; // Detect attribute value character (double quote) struct AttPurePred2 { static bool test(char ch) { return lutAttributeData2Pure[u8(ch)]; } }; // Insert coded character, using UTF8 static inline void insertUTF8char(char*& text, unsigned long code) { if (code < 0x80) { // 1 byte sequence text[0] = char(code); text += 1; } else if (code < 0x800) {// 2 byte sequence text[1] = char((code | 0x80) & 0xBF); code >>= 6; text[0] = char (code | 0xC0); text += 2; } else if (code < 0x10000) { // 3 byte sequence text[2] = char((code | 0x80) & 0xBF); code >>= 6; text[1] = char((code | 0x80) & 0xBF); code >>= 6; text[0] = char (code | 0xE0); text += 3; } else if (code < 0x110000) { // 4 byte sequence text[3] = char((code | 0x80) & 0xBF); code >>= 6; text[2] = char((code | 0x80) & 0xBF); code >>= 6; text[1] = char((code | 0x80) & 0xBF); code >>= 6; text[0] = char (code | 0xF0); text += 4; } else { // Invalid, only codes up to 0x10FFFF are allowed in Unicode throw ParseError("invalid numeric character entity", text); } } template static inline bool next(const char* p) { return (p[0] == C0) && (p[1] == C1); } template static inline bool next(const char* p) { return (p[0] == C0) && (p[1] == C1) && (p[2] == C2); } template static inline bool next(const char* p) { return (p[0] == C0) && (p[1] == C1) && (p[2] == C2) && (p[3] == C3); } template static inline bool next(const char* p) { return (p[0] == C0) && (p[1] == C1) && (p[2] == C2) && (p[3] == C3) && (p[4] == C4) && (p[5] == C5); } // Skip characters until predicate evaluates to true template static inline void skip(char*& text) { char* tmp = text; while (StopPred::test(*tmp)) ++tmp; text = tmp; } // Skip characters until predicate evaluates to true while doing the following: // - replacing XML character entity references with proper characters // (' & " < > &#...;) // - condensing whitespace sequences to single space character template static inline char* skipAndExpand(char*& text) { // If entity translation, whitespace condense and whitespace // trimming is disabled, use plain skip. if ( (FLAGS & noEntityTranslation) && !(FLAGS & normalizeWhitespace) && !(FLAGS & trimWhitespace)) { skip(text); return text; } // Use simple skip until first modification is detected skip(text); // Use translation skip char* src = text; char* dest = src; while (StopPred::test(*src)) { // Test if replacement is needed if (!(FLAGS & noEntityTranslation) && (src[0] == '&')) { switch (src[1]) { case 'a': // & ' if (next<'m','p',';'>(&src[2])) { *dest = '&'; ++dest; src += 5; continue; } if (next<'p','o','s',';'>(&src[2])) { *dest = '\''; ++dest; src += 6; continue; } break; case 'q': // " if (next<'u','o','t',';'>(&src[2])) { *dest = '"'; ++dest; src += 6; continue; } break; case 'g': // > if (next<'t',';'>(&src[2])) { *dest = '>'; ++dest; src += 4; continue; } break; case 'l': // < if (next<'t',';'>(&src[2])) { *dest = '<'; ++dest; src += 4; continue; } break; case '#': // &#...; - assumes ASCII if (src[2] == 'x') { unsigned long code = 0; src += 3; // skip &#x while (true) { u8 digit = lutDigits[u8(*src)]; if (digit == 0xFF) break; code = code * 16 + digit; ++src; } insertUTF8char(dest, code); } else { unsigned long code = 0; src += 2; // skip &# while (1) { u8 digit = lutDigits[u8(*src)]; if (digit == 0xFF) break; code = code * 10 + digit; ++src; } insertUTF8char(dest, code); } if (*src != ';') { throw ParseError("expected ;", src); } ++src; continue; default: // Something else, ignore, just copy '&' verbatim break; } } // Test if condensing is needed if ((FLAGS & normalizeWhitespace) && (WhitespacePred::test(*src))) { *dest++ = ' '; // single space in dest ++src; // skip first whitespace char // Skip remaining whitespace chars while (WhitespacePred::test(*src)) ++src; continue; } // No replacement, only copy character *dest++ = *src++; } // Return new end text = src; return dest; } static inline void skipBOM(char*& text) { if (next(text)) { text += 3; // skip utf-8 bom } } template class Parser { HANDLER& handler; public: Parser(HANDLER& handler_, char* text) : handler(handler_) { skipBOM(text); while (true) { // Skip whitespace before node skip(text); if (*text == 0) break; if (*text != '<') { throw ParseError("expected <", text); } ++text; // skip '<' parseNode(text); } } private: // Parse XML declaration ((text); // skip ws before attributes or ?> parseAttributes(text); handler.declarationStop(); // skip ?> if (!next<'?','>'>(text)) { throw ParseError("expected ?>", text); } text += 2; } // Parse XML comment (' } void parseDoctype(char*& text) { char* value = text; // remember value start // skip to > while (*text != '>') { switch (*text) { case '[': { // If '[' encountered, scan for matching ending // ']' using naive algorithm with depth. This // works for all W3C test files except for 2 // most wicked. ++text; // skip '[' int depth = 1; while (depth > 0) { switch (*text) { case char('['): ++depth; break; case char(']'): --depth; break; case 0: throw ParseError( "unexpected end of data", text); } ++text; } break; } case '\0': throw ParseError("unexpected end of data", text); default: ++text; } } handler.doctype(string_ref(value, text)); text += 1; // skip '>' } void parsePI(char*& text) { // Extract PI target name char* name = text; skip(text); char* nameEnd = text; if (name == nameEnd) { throw ParseError("expected PI target", text); } // Skip whitespace between pi target and pi skip(text); // Skip to '?>' char* value = text; // Remember start of pi while (!next<'?','>'>(text)) { if (*text == 0) { throw ParseError("unexpected end of data", text); } ++text; } // Set pi value (verbatim, no entity expansion or ws normalization) handler.procInstr(string_ref(name, nameEnd), string_ref(value, text)); text += 2; // skip '?>' } void parseText(char*& text, char* contentsStart) { // Backup to contents start if whitespace trimming is disabled if (!(FLAGS & trimWhitespace)) { text = contentsStart; } // Skip until end of data char* value = text; char* end = (FLAGS & normalizeWhitespace) ? skipAndExpand(text) : skipAndExpand(text); // Trim trailing whitespace; leading was already trimmed by // whitespace skip after > if (FLAGS & trimWhitespace) { if (FLAGS & normalizeWhitespace) { // Whitespace is already condensed to single // space characters by skipping function, so // just trim 1 char off the end. if (end[-1] == ' ') { --end; } } else { // Backup until non-whitespace character is found while (WhitespacePred::test(end[-1])) { --end; } } } // Handle text, but only if non-empty. auto len = end - value; if (len) handler.text(string_ref(value, len)); } void parseCdata(char*& text) { // Skip until end of cdata char* value = text; while (!next<']',']','>'>(text)) { if (text[0] == 0) { throw ParseError("unexpected end of data", text); } ++text; } handler.cdata(string_ref(value, text)); text += 3; // skip ]]> } void parseElement(char*& text) { // Extract element name char* name = text; skip(text); char* nameEnd = text; if (name == nameEnd) { throw ParseError("expected element name", text); } handler.start(string_ref(name, nameEnd)); skip(text); // skip ws before attributes or > parseAttributes(text); // Determine ending type if (*text == '>') { ++text; parseNodeContents(text); } else if (*text == '/') { handler.stop(); ++text; if (*text != '>') { throw ParseError("expected >", text); } ++text; } else { throw ParseError("expected >", text); } } // Determine node type, and parse it void parseNode(char*& text) { switch (text[0]) { case '?': // (text) || next<'X','M','L'>(text)) && WhitespacePred::test(text[3])) { // '(&text[2])) { // '(&text[2]) && WhitespacePred::test(text[8])) { // '') { if (*text == 0) { throw ParseError( "unexpected end of data", text); } ++text; } ++text; // skip '>' break; default: // <... parseElement(text); break; } } // Parse contents of the node - children, data etc. void parseNodeContents(char*& text) { while (true) { char* contentsStart = text; // start before ws is skipped skip(text); // Skip ws between > and contents afterText: // After parseText() jump here instead of continuing // the loop, because skipping whitespace is unnecessary. switch (*text) { case '<': // Node closing or child node if (text[1] == '/') { // Node closing text += 2; // skip '(text); // TODO validate closing tag?? handler.stop(); // Skip remaining whitespace after node name skip(text); if (*text != '>') { throw ParseError("expected >", text); } ++text; // skip '>' return; } else { // Child node ++text; // skip '<' parseNode(text); } break; case '\0': throw ParseError("unexpected end of data", text); default: parseText(text, contentsStart); goto afterText; } } } // Parse XML attributes of the node void parseAttributes(char*& text) { // For all attributes while (AttributeNamePred::test(*text)) { // Extract attribute name char* name = text; ++text; // Skip first character of attribute name skip(text); char* nameEnd = text; if (name == nameEnd) { throw ParseError("expected attribute name", name); } skip(text); // skip ws after name if (*text != '=') { throw ParseError("expected =", text); } ++text; // skip = skip(text); // skip ws after = // Skip quote and remember if it was ' or " char quote = *text; if (quote != '\'' && quote != '"') { throw ParseError("expected ' or \"", text); } ++text; // Extract attribute value and expand char refs in it // No whitespace normalization in attributes static const int FLAGS2 = FLAGS & ~normalizeWhitespace; char* value = text; char* valueEnd = (quote == '\'') ? skipAndExpand(text) : skipAndExpand(text); handler.attribute(string_ref(name, nameEnd), string_ref(value, valueEnd)); // Make sure that end quote is present if (*text != quote) { throw ParseError("expected ' or \"", text); } ++text; // skip quote skip(text); // skip ws after value } } }; } // namespace internal template inline void parse(HANDLER& handler, char* xml) { internal::Parser parser(handler, xml); } } // namespace rapidsax #endif openmsx-0.10.0/src/utils/CRC16.hh0000644000175000017500000000736012262345041017100 0ustar manuelmanuel00000000000000#ifndef CRC16_HH #define CRC16_HH #include #include namespace openmsx { /** * This class calculates CRC numbers for the polygon * x^16 + x^12 + x^5 + 1 */ class CRC16 { public: /** Create CRC16 with an optional initial value */ explicit CRC16(uint16_t initialCRC = 0xFFFF) { init(initialCRC); } /** (Re)initialize the current value */ void init(uint16_t initialCRC) { crc = initialCRC; } /** (Re)initialize with a short initial sequence. * The initial value is guaranteed to be computed at compile time. */ template void init() { static const uint16_t T0 = 0xffff; static const uint16_t T1 = CT_CRC16::value; init(T1); } template void init() { static const uint16_t T0 = 0xffff; static const uint16_t T1 = CT_CRC16::value; static const uint16_t T2 = CT_CRC16::value; init(T2); } template void init() { static const uint16_t T0 = 0xffff; static const uint16_t T1 = CT_CRC16::value; static const uint16_t T2 = CT_CRC16::value; static const uint16_t T3 = CT_CRC16::value; init(T3); } template void init() { static const uint16_t T0 = 0xffff; static const uint16_t T1 = CT_CRC16::value; static const uint16_t T2 = CT_CRC16::value; static const uint16_t T3 = CT_CRC16::value; static const uint16_t T4 = CT_CRC16::value; init(T4); } /** Update CRC with one byte */ void update(uint8_t value) { // Classical byte-at-a-time algorithm by Dilip V. Sarwate crc = (crc << 8) ^ tab[0][(crc >> 8) ^ value]; } /** For large blocks (e.g. 512 bytes) this routine is approx 5x faster * than calling the method above in a loop. */ void update(const uint8_t* data, size_t size) { // Based on: // Slicing-by-4 and slicing-by-8 algorithms by Michael E. // Kounavis and Frank L. Berry from Intel Corp. // http://www.intel.com/technology/comms/perfnet/download/CRC_generators.pdf // and the implementation by Peter Kankowski found on: // http://www.strchr.com/crc32_popcnt // I transformed the code from CRC32 to CRC16 (this was not // trivial because both CRCs use a different convention about // bit order). I also made the code work on bigendian hosts. unsigned c = crc; // 32-bit are faster than 16-bit calculations // on x86 and many other modern architectures // calculate the bulk of the data 8 bytes at a time for (auto n = size / 8; n; --n) { c = tab[7][data[0] ^ (c >> 8)] ^ tab[6][data[1] ^ (c & 255)] ^ tab[5][data[2]] ^ tab[4][data[3]] ^ tab[3][data[4]] ^ tab[2][data[5]] ^ tab[1][data[6]] ^ tab[0][data[7]]; data += 8; } // calculate the remaining bytes in the usual way for (size &= 7; size; --size) { c = uint16_t(c << 8) ^ tab[0][(c >> 8) ^ *data++]; } crc = c; // store back in a 16-bit result } /** Get current CRC value */ uint16_t getValue() const { return crc; } private: uint16_t crc; static const uint16_t tab[8][256]; // The Stuff below is template magic to perform the following // computation at compile-time: // for (int i = 8; i < 16; ++i) { // crc = (crc << 1) ^ ((((crc ^ (data << i)) & 0x8000) ? 0x1021 : 0)); // } template struct CT_H { static const uint16_t D = uint16_t(C << 1) ^ (((C ^ V) & 0x8000) ? 0x1021 : 0); static const uint16_t value = CT_H::value; }; template struct CT_H { static const uint16_t value = C; }; template struct CT_CRC16 : CT_H {}; }; } // namespace openmsx #endif openmsx-0.10.0/src/utils/sdlwin32.hh0000644000175000017500000000026512262345041017764 0ustar manuelmanuel00000000000000#ifndef SDLWIN32_HH #define SDLWIN32_HH #ifdef _WIN32 #include namespace openmsx { HWND getSDLWindowHandle(); } // namespace openmsx #endif #endif // SDLWIN32_HH openmsx-0.10.0/src/utils/StringOp.cc0000644000175000017500000002566612262345041020066 0ustar manuelmanuel00000000000000#include "StringOp.hh" #include "MSXException.hh" #include #include #include "cstdlibp.hh" #include using std::advance; using std::equal; using std::string; using std::transform; using std::vector; using std::set; namespace StringOp { // class Builder Builder::Builder() { } Builder::~Builder() { } Builder& Builder::operator<<(const std::string& t) { buf += t; return *this; } Builder& Builder::operator<<(string_ref t) { buf.append(t.data(), t.size()); return *this; } Builder& Builder::operator<<(const char* t) { buf += t; return *this; } Builder& Builder::operator<<(unsigned char t) { return operator<<(unsigned(t)); } Builder& Builder::operator<<(unsigned short t) { buf += toString(t); return *this; } Builder& Builder::operator<<(unsigned t) { buf += toString(t); return *this; } Builder& Builder::operator<<(unsigned long t) { buf += toString(t); return *this; } Builder& Builder::operator<<(unsigned long long t) { buf += toString(t); return *this; } Builder& Builder::operator<<(char t) { buf += t; return *this; } Builder& Builder::operator<<(short t) { buf += toString(t); return *this; } Builder& Builder::operator<<(int t) { buf += toString(t); return *this; } Builder& Builder::operator<<(long t) { buf += toString(t); return *this; } Builder& Builder::operator<<(long long t) { buf += toString(t); return *this; } Builder& Builder::operator<<(float t) { buf += toString(t); return *this; } Builder& Builder::operator<<(double t) { buf += toString(t); return *this; } // Returns a fast type that is (at least) big enough to hold the absolute value // of values of the given type. (It always returns 'unsigned' except for 64-bit // integers it returns unsigned long long). template struct FastUnsigned { typedef unsigned type; }; template<> struct FastUnsigned { typedef unsigned long long type; }; template<> struct FastUnsigned { typedef unsigned long long type; }; template<> struct FastUnsigned { typedef unsigned long type; }; template<> struct FastUnsigned { typedef unsigned long type; }; // This does the equivalent of // unsigned u = (t < 0) ? -t : t; // but it avoids a compiler warning on the operations // 't < 0' and '-t' // when 't' is actually an unsigned type. template struct AbsHelper; template<> struct AbsHelper { template inline typename FastUnsigned::type operator()(T t) const { return (t < 0) ? -t : t; } }; template<> struct AbsHelper { template inline typename FastUnsigned::type operator()(T t) const { return t; } }; // Does the equivalent of if (t < 0) *--p = '-'; // but it avoids a compiler warning on 't < 0' when 't' is an unsigned type. template struct PutSign; template<> struct PutSign { template inline void operator()(T t, char*& p) const { if (t < 0) *--p = '-'; } }; template<> struct PutSign { template inline void operator()(T /*t*/, char*& /*p*/) const { // nothing } }; // This routine is inspired by boost::lexical_cast. It's much faster than a // generic version using std::stringstream. See this page for some numbers: // http://www.boost.org/doc/libs/1_47_0/libs/conversion/lexical_cast.htm#performance template static inline string toStringImpl(T t) { static const bool IS_SIGNED = std::numeric_limits::is_signed; static const unsigned BUF_SIZE = 1 + std::numeric_limits::digits10 + (IS_SIGNED ? 1 : 0); char buf[BUF_SIZE]; char* p = &buf[BUF_SIZE]; AbsHelper absHelper; typename FastUnsigned::type a = absHelper(t); do { *--p = '0' + (a % 10); a /= 10; } while (a); PutSign putSign; putSign(t, p); return string(p, &buf[BUF_SIZE] - p); } string toString(long long a) { return toStringImpl(a); } string toString(unsigned long long a) { return toStringImpl(a); } string toString(long a) { return toStringImpl(a); } string toString(unsigned long a) { return toStringImpl(a); } string toString(int a) { return toStringImpl(a); } string toString(unsigned a) { return toStringImpl(a); } string toString(short a) { return toStringImpl(a); } string toString(unsigned short a) { return toStringImpl(a); } string toString(char a) { return string(1, a); } string toString(signed char a) { return string(1, a); } string toString(unsigned char a) { return string(1, a); } string toString(bool a) { return string(1, '0' + a); } static inline char hexDigit(unsigned x) { return (x < 10) ? ('0' + x) : ('a' + x - 10); } string toHexString(unsigned x, unsigned width) { assert((0 < width) && (width <= 8)); char buf[8]; char* p = &buf[8]; int i = width; do { *--p = hexDigit(x & 15); x >>= 4; } while (--i); return string(p, width); } int stringToInt(const string& str) { return strtol(str.c_str(), nullptr, 0); } bool stringToInt(const string& str, int& result) { char* endptr; result = strtol(str.c_str(), &endptr, 0); return *endptr == '\0'; } unsigned stringToUint(const string& str) { return strtoul(str.c_str(), nullptr, 0); } bool stringToUint(const string& str, unsigned& result) { char* endptr; result = strtoul(str.c_str(), &endptr, 0); return *endptr == '\0'; } uint64_t stringToUint64(const string& str) { return strtoull(str.c_str(), nullptr, 0); } bool stringToBool(string_ref str) { if (str == "1") return true; if ((str.size() == 4) && (strncasecmp(str.data(), "true", 4) == 0)) return true; if ((str.size() == 3) && (strncasecmp(str.data(), "yes", 3) == 0)) return true; return false; } double stringToDouble(const string& str) { return strtod(str.c_str(), nullptr); } bool stringToDouble(const string& str, double& result) { char* endptr; result = strtod(str.c_str(), &endptr); return *endptr == '\0'; } string toLower(string_ref str) { string result = str.str(); transform(result.begin(), result.end(), result.begin(), ::tolower); return result; } bool startsWith(string_ref total, string_ref part) { return total.starts_with(part); } bool startsWith(string_ref total, char part) { return !total.empty() && (total.front() == part); } bool endsWith(string_ref total, string_ref part) { return total.ends_with(part); } bool endsWith(string_ref total, char part) { return !total.empty() && (total.back() == part); } void trimRight(string& str, const char* chars) { auto pos = str.find_last_not_of(chars); if (pos != string::npos) { str.erase(pos + 1); } else { str.clear(); } } void trimRight(string& str, char chars) { auto pos = str.find_last_not_of(chars); if (pos != string::npos) { str.erase(pos + 1); } else { str.clear(); } } void trimRight(string_ref& str, string_ref chars) { while (!str.empty() && (chars.find(str.back()) != string_ref::npos)) { str.pop_back(); } } void trimRight(string_ref& str, char chars) { while (!str.empty() && (str.back() == chars)) { str.pop_back(); } } void trimLeft (string& str, const char* chars) { str.erase(0, str.find_first_not_of(chars)); } void trimLeft (string_ref& str, string_ref chars) { while (!str.empty() && (chars.find(str[0]) != string_ref::npos)) { str.pop_front(); } } void splitOnFirst(string_ref str, string_ref chars, string_ref& first, string_ref& last) { auto pos = str.find_first_of(chars); if (pos == string_ref::npos) { first = str; last.clear(); } else { first = str.substr(0, pos); last = str.substr(pos + 1); } } void splitOnFirst(string_ref str, char chars, string_ref& first, string_ref& last) { auto pos = str.find_first_of(chars); if (pos == string_ref::npos) { first = str; last.clear(); } else { first = str.substr(0, pos); last = str.substr(pos + 1); } } void splitOnLast(string_ref str, string_ref chars, string_ref& first, string_ref& last) { auto pos = str.find_last_of(chars); if (pos == string_ref::npos) { first.clear(); last = str; } else { first = str.substr(0, pos); last = str.substr(pos + 1); } } void splitOnLast(string_ref str, char chars, string_ref& first, string_ref& last) { auto pos = str.find_last_of(chars); if (pos == string_ref::npos) { first.clear(); last = str; } else { first = str.substr(0, pos); last = str.substr(pos + 1); } } vector split(string_ref str, char chars) { vector result; while (!str.empty()) { string_ref first, last; splitOnFirst(str, chars, first, last); result.push_back(first); str = last; } return result; } string join(const vector& elems, char separator) { if (elems.empty()) return string(); auto it = elems.begin(); Builder result; result << *it; for (++it; it != elems.end(); ++it) { result << separator; result << *it; } return result; } static unsigned parseNumber(string_ref str) { // trimRight only: strtoul can handle leading spaces trimRight(str, " \t"); if (str.empty()) { throw openmsx::MSXException("Invalid integer: empty string"); } string_ref::size_type idx; unsigned result = stoi(str, &idx); if (idx != str.size()) { throw openmsx::MSXException("Invalid integer: " + str); } return result; } static void insert(unsigned x, set& result, unsigned min, unsigned max) { if ((x < min) || (x > max)) { throw openmsx::MSXException("Out of range"); } result.insert(x); } static void parseRange2(string_ref str, set& result, unsigned min, unsigned max) { // trimRight only: here we only care about all spaces trimRight(str, " \t"); if (str.empty()) return; auto pos = str.find('-'); if (pos == string_ref::npos) { insert(parseNumber(str), result, min, max); } else { unsigned begin = parseNumber(str.substr(0, pos)); unsigned end = parseNumber(str.substr(pos + 1)); if (end < begin) { std::swap(begin, end); } for (unsigned i = begin; i <= end; ++i) { insert(i, result, min, max); } } } set parseRange(string_ref str, unsigned min, unsigned max) { set result; while (true) { auto next = str.find(','); string_ref sub = (next == string_ref::npos) ? str : str.substr(0, next++); parseRange2(sub, result, min, max); if (next == string_ref::npos) break; str = str.substr(next); } return result; } #if defined(__APPLE__) std::string fromCFString(CFStringRef str) { // Try the quick route first. const char *cstr = CFStringGetCStringPtr(str, kCFStringEncodingUTF8); if (cstr) { // String was already in UTF8 encoding. return std::string(cstr); } // Convert to UTF8 encoding. CFIndex len = CFStringGetLength(str); CFRange range = CFRangeMake(0, len); CFIndex usedBufLen = 0; CFStringGetBytes( str, range, kCFStringEncodingUTF8, '?', false, nullptr, len, &usedBufLen); UInt8 buffer[usedBufLen]; CFStringGetBytes( str, range, kCFStringEncodingUTF8, '?', false, buffer, len, &usedBufLen); return std::string(reinterpret_cast(buffer), usedBufLen); } #endif } // namespace StringOp openmsx-0.10.0/src/utils/snappy.cc0000644000175000017500000005072612262345041017626 0ustar manuelmanuel00000000000000#include "snappy.hh" #include "aligned.hh" #include "likely.hh" #include "endian.hh" #include "build-info.hh" #include #include #include #include #include using namespace openmsx; namespace snappy { enum { LITERAL = 0, COPY_1_BYTE_OFFSET = 1, // 3 bit length + 3 bits of offset in opcode COPY_2_BYTE_OFFSET = 2, COPY_4_BYTE_OFFSET = 3 }; static inline void unalignedCopy64(const char* src, char* dst) { if (sizeof(void*) == 8) { unalignedStore64(dst + 0, unalignedLoad64(src + 0)); } else { // This can be more efficient than unalignedLoad64 + // unalignedStore64 on some platforms, in particular ARM. unalignedStore32(dst + 0, unalignedLoad32(src + 0)); unalignedStore32(dst + 4, unalignedLoad32(src + 4)); } } static inline void unalignedCopy128(const char* src, char* dst) { unalignedCopy64(src + 0, dst + 0); unalignedCopy64(src + 8, dst + 8); } ///////////////// // Copy "len" bytes from "src" to "op", one byte at a time. Used for // handling COPY operations where the input and output regions may // overlap. For example, suppose: // src == "ab" // op == src + 2 // len == 20 // After incrementalCopy(src, op, len), the result will have // eleven copies of "ab" // ababababababababababab // Note that this does not match the semantics of either memcpy() // or memmove(). static inline void incrementalCopy(const char* src, char* op, ptrdiff_t len) { assert(len > 0); do { *op++ = *src++; } while (--len); } // Equivalent to incrementalCopy() except that it can write up to ten extra // bytes after the end of the copy, and that it is faster. // // The main part of this loop is a simple copy of eight bytes at a time until // we've copied (at least) the requested amount of bytes. However, if op and // src are less than eight bytes apart (indicating a repeating pattern of // length < 8), we first need to expand the pattern in order to get the correct // results. For instance, if the buffer looks like this, with the eight-byte // and patterns marked as intervals: // // abxxxxxxxxxxxx // [------] src // [------] op // // a single eight-byte copy from to will repeat the pattern once, // after which we can move two bytes without moving : // // ababxxxxxxxxxx // [------] src // [------] op // // and repeat the exercise until the two no longer overlap. // // This allows us to do very well in the special case of one single byte // repeated many times, without taking a big hit for more general cases. // // The worst case of extra writing past the end of the match occurs when // op - src == 1 and len == 1; the last copy will read from byte positions // [0..7] and write to [4..11], whereas it was only supposed to write to // position 1. Thus, ten excess bytes. static const int MAX_INCR_COPY_OVERFLOW = 10; static inline void incrementalCopyFast(const char* src, char* op, ptrdiff_t len) { while (op - src < 8) { unalignedCopy64(src, op); len -= op - src; op += op - src; } while (len > 0) { unalignedCopy64(src, op); src += 8; op += 8; len -= 8; } } static inline uint32_t loadNBytes(const void* p, unsigned n) { // Mapping from i in range [0,4] to a mask to extract the bottom i bytes static const uint32_t wordmask[] = { 0, 0xff, 0xffff, 0xffffff, 0xffffffff }; return Endian::read_UA_L32(p) & wordmask[n]; } // Data stored per entry in lookup table: // Range Bits-used Description // ------------------------------------ // 1..64 0..7 Literal/copy length encoded in opcode byte // 0..7 8..10 Copy offset encoded in opcode byte / 256 // 0..4 11..13 Extra bytes after opcode // // We use eight bits for the length even though 7 would have sufficed // because of efficiency reasons: // (1) Extracting a byte is faster than a bit-field // (2) It properly aligns copy offset so we do not need a <<8 // See original code for a generator for this table. static const uint16_t charTable[256] = { 0x0001, 0x0804, 0x1001, 0x2001, 0x0002, 0x0805, 0x1002, 0x2002, 0x0003, 0x0806, 0x1003, 0x2003, 0x0004, 0x0807, 0x1004, 0x2004, 0x0005, 0x0808, 0x1005, 0x2005, 0x0006, 0x0809, 0x1006, 0x2006, 0x0007, 0x080a, 0x1007, 0x2007, 0x0008, 0x080b, 0x1008, 0x2008, 0x0009, 0x0904, 0x1009, 0x2009, 0x000a, 0x0905, 0x100a, 0x200a, 0x000b, 0x0906, 0x100b, 0x200b, 0x000c, 0x0907, 0x100c, 0x200c, 0x000d, 0x0908, 0x100d, 0x200d, 0x000e, 0x0909, 0x100e, 0x200e, 0x000f, 0x090a, 0x100f, 0x200f, 0x0010, 0x090b, 0x1010, 0x2010, 0x0011, 0x0a04, 0x1011, 0x2011, 0x0012, 0x0a05, 0x1012, 0x2012, 0x0013, 0x0a06, 0x1013, 0x2013, 0x0014, 0x0a07, 0x1014, 0x2014, 0x0015, 0x0a08, 0x1015, 0x2015, 0x0016, 0x0a09, 0x1016, 0x2016, 0x0017, 0x0a0a, 0x1017, 0x2017, 0x0018, 0x0a0b, 0x1018, 0x2018, 0x0019, 0x0b04, 0x1019, 0x2019, 0x001a, 0x0b05, 0x101a, 0x201a, 0x001b, 0x0b06, 0x101b, 0x201b, 0x001c, 0x0b07, 0x101c, 0x201c, 0x001d, 0x0b08, 0x101d, 0x201d, 0x001e, 0x0b09, 0x101e, 0x201e, 0x001f, 0x0b0a, 0x101f, 0x201f, 0x0020, 0x0b0b, 0x1020, 0x2020, 0x0021, 0x0c04, 0x1021, 0x2021, 0x0022, 0x0c05, 0x1022, 0x2022, 0x0023, 0x0c06, 0x1023, 0x2023, 0x0024, 0x0c07, 0x1024, 0x2024, 0x0025, 0x0c08, 0x1025, 0x2025, 0x0026, 0x0c09, 0x1026, 0x2026, 0x0027, 0x0c0a, 0x1027, 0x2027, 0x0028, 0x0c0b, 0x1028, 0x2028, 0x0029, 0x0d04, 0x1029, 0x2029, 0x002a, 0x0d05, 0x102a, 0x202a, 0x002b, 0x0d06, 0x102b, 0x202b, 0x002c, 0x0d07, 0x102c, 0x202c, 0x002d, 0x0d08, 0x102d, 0x202d, 0x002e, 0x0d09, 0x102e, 0x202e, 0x002f, 0x0d0a, 0x102f, 0x202f, 0x0030, 0x0d0b, 0x1030, 0x2030, 0x0031, 0x0e04, 0x1031, 0x2031, 0x0032, 0x0e05, 0x1032, 0x2032, 0x0033, 0x0e06, 0x1033, 0x2033, 0x0034, 0x0e07, 0x1034, 0x2034, 0x0035, 0x0e08, 0x1035, 0x2035, 0x0036, 0x0e09, 0x1036, 0x2036, 0x0037, 0x0e0a, 0x1037, 0x2037, 0x0038, 0x0e0b, 0x1038, 0x2038, 0x0039, 0x0f04, 0x1039, 0x2039, 0x003a, 0x0f05, 0x103a, 0x203a, 0x003b, 0x0f06, 0x103b, 0x203b, 0x003c, 0x0f07, 0x103c, 0x203c, 0x0801, 0x0f08, 0x103d, 0x203d, 0x1001, 0x0f09, 0x103e, 0x203e, 0x1801, 0x0f0a, 0x103f, 0x203f, 0x2001, 0x0f0b, 0x1040, 0x2040 }; static const size_t SCRATCH_SIZE = 16; void uncompress(const char* input, size_t inLen, char* output, size_t outLen) { const char* ip = input; const char* ipLimit = input + inLen - SCRATCH_SIZE; char* op = output;; char* opLimit = output + outLen; while (ip != ipLimit) { unsigned char c = *ip++; if ((c & 0x3) == LITERAL) { size_t literalLen = (c >> 2) + 1; size_t outLeft = opLimit - op; if (literalLen <= 16 && outLeft >= 16) { // Fast path, used for the majority (about 95%) // of invocations. unalignedCopy128(ip, op); op += literalLen; ip += literalLen; continue; } if (unlikely(literalLen >= 61)) { // Long literal. size_t literalLenLen = literalLen - 60; literalLen = loadNBytes(ip, unsigned(literalLenLen)) + 1; ip += literalLenLen; } memcpy(op, ip, literalLen); op += literalLen; ip += literalLen; } else { uint32_t entry = charTable[c]; uint32_t trailer = loadNBytes(ip, entry >> 11); uint32_t length = entry & 0xff; ip += entry >> 11; // offset/256 is encoded in bits 8..10. By just // fetching those bits, we get copyOffset (since the // bit-field starts at bit 8). size_t offset = (entry & 0x700) + trailer; size_t outLeft = opLimit - op; const char* src = op - offset; if (length <= 16 && offset >= 8 && outLeft >= 16) { // Fast path, used for the majority (70-80%) of // dynamic invocations. unalignedCopy128(src, op); } else { if (outLeft >= length + MAX_INCR_COPY_OVERFLOW) { incrementalCopyFast(src, op, length); } else { incrementalCopy(src, op, length); } } op += length; } } } ///////////////// // Any hash function will produce a valid compressed bitstream, but a good hash // function reduces the number of collisions and thus yields better compression // for compressible input, and more speed for incompressible input. Of course, // it doesn't hurt if the hash function is reasonably fast either, as it gets // called a lot. template static inline uint32_t hashBytes(uint32_t bytes) { return (bytes * 0x1e35a7bd) >> SHIFT; } template static inline uint32_t hash(const char* p) { return hashBytes(unalignedLoad32(p)); } // For 0 <= offset <= 4, getUint32AtOffset(getEightBytesAt(p), offset) will // equal unalignedLoad32(p + offset). Motivation: On x86-64 hardware we have // empirically found that overlapping loads such as // unalignedLoad32(p) ... unalignedLoad32(p+1) ... unalignedLoad32(p+2) // are slower than unalignedLoad64(p) followed by shifts and casts to uint32_t. // // We have different versions for 64- and 32-bit; ideally we would avoid the // two functions and just inline the unalignedLoad64 call into // getUint32AtOffset, but GCC (at least not as of 4.6) is seemingly not clever // enough to avoid loading the value multiple times then. For 64-bit, the load // is done when getEightBytesAt() is called, whereas for 32-bit, the load is // done at getUint32AtOffset() time. template class EightBytesReference; template<> class EightBytesReference<8> { public: void setAddress(const char* ptr) { data = unalignedLoad64(ptr); } template uint32_t getUint32AtOffset() const { static_assert((OFFSET >= 0) && (OFFSET <= 4), "must be in [0..4]"); int shift = OPENMSX_BIGENDIAN ? (32 - 8 * OFFSET) : ( 8 * OFFSET); return data >> shift; } private: uint64_t data; }; template<> class EightBytesReference<4> { public: void setAddress(const char* ptr_) { ptr = ptr_; } template uint32_t getUint32AtOffset() const { static_assert((OFFSET >= 0) && (OFFSET <= 4), "must be in [0..4]"); return unalignedLoad32(ptr + OFFSET); } private: const char* ptr; }; // Count the number of trailing zero bytes (=trailing zero bits divided by 8). // Returns an undefined value if n == 0. template static inline unsigned ctzDiv8(T n) { static const unsigned DIV = 8; #if (defined(__i386__) || defined(__x86_64__)) && \ ((__GNUC__ >= 4) || ((__GNUC__ == 3) && (__GNUC_MINOR >= 3))) // Gcc-3.4 or above have the following builtins. Though we only want to // use them if they are very fast. E.g. on x86 they compile to a single // 'bsf' instruction. If they have to be emulated in software then the // fallback code below is likely faster (because it skips part of the // full ctz calculation). // TODO on vc++ we could use // _BitScanForward and _BitScanForward64 // and possibly other compilers have something similar. if (sizeof(T) <= 4) { return __builtin_ctz(n) / DIV; } else { return __builtin_ctzll(n) / DIV; } #else // Classical ctz routine, but skip the last 3 (for DIV=8) iterations. unsigned bits = 8 * sizeof(T); unsigned r = (bits - 1) / DIV; for (unsigned shift = bits / 2; shift >= DIV; shift /= 2) { if (T x = n << shift) { n = x; r -= shift / DIV; } } return r; #endif } // Return the largest n such that // // s1[0,n-1] == s2[0,n-1] // and n <= (s2Limit - s2). // // Does not read *s2Limit or beyond. // Does not read *(s1 + (s2Limit - s2)) or beyond. // Requires that s2Limit >= s2. // // This implementation is tuned for 64-bit, little-endian machines. But it // also works fine on 32-bit and/or big-endian machines. // search either 4 or 8 bytes at-a-time template struct FindMatchUnit; template<> struct FindMatchUnit<4> { typedef uint32_t type; }; template<> struct FindMatchUnit<8> { typedef uint64_t type; }; static inline int findMatchLength(const char* s1, const char* s2, const char* s2Limit) { assert(s2Limit >= s2); int matched = 0; // Find out how long the match is. We loop over the data N bits at a // time until we find a N-bit block that doesn't match, Then (only on // little endian machines) we find the first non-matching bit and use // that to calculate the total length of the match. typedef FindMatchUnit::type T; while (likely(s2 <= s2Limit - sizeof(T))) { T l2 = unalignedLoad(s2); T l1 = unalignedLoad(s1 + matched); if (unlikely(l2 == l1)) { s2 += sizeof(T); matched += sizeof(T); } else { if (OPENMSX_BIGENDIAN) break; // On current (mid-2008) Opteron models there is a 3% // more efficient code sequence to find the first // non-matching byte. However, what follows is ~10% // better on Intel Core 2 and newer, and we expect // AMD's bsf instruction to improve. return matched + ctzDiv8(l2 ^ l1); } } while ((s2 < s2Limit) && (s1[matched] == *s2)) { ++s2; ++matched; } return matched; } template static inline char* emitLiteral(char* op, const char* literal, int len) { int n = len - 1; // Zero-length literals are disallowed if (n < 60) { // Fits in tag byte *op++ = LITERAL | (n << 2); // The vast majority of copies are below 16 bytes, for which a // call to memcpy is overkill. This fast path can sometimes // copy up to 15 bytes too much, but that is okay in the // main loop, since we have a bit to go on for both sides: // // - The input will always have INPUT_MARGIN_BYTES = 15 extra // available bytes, as long as we're in the main loop, and // if not, ALLOW_FAST_PATH = false. // - The output will always have 32 spare bytes (see // maxCompressedLength()). if (ALLOW_FAST_PATH && len <= 16) { unalignedCopy128(literal, op); return op + len; } } else { // Encode in upcoming bytes char* base = op; op++; int count = 0; do { *op++ = n & 0xff; n >>= 8; count++; } while (n > 0); assert(count >= 1); assert(count <= 4); *base = LITERAL | ((59 + count) << 2); } memcpy(op, literal, len); return op + len; } static inline char* emitCopyLessThan64(char* op, size_t offset, int len) { assert(len <= 64); assert(len >= 4); assert(offset < 65536); if ((len < 12) && (offset < 2048)) { size_t lenMinus4 = len - 4; assert(lenMinus4 < 8); // Must fit in 3 bits *op++ = char(COPY_1_BYTE_OFFSET + ((lenMinus4) << 2) + ((offset >> 8) << 5)); *op++ = offset & 0xff; } else { *op++ = COPY_2_BYTE_OFFSET + ((len - 1) << 2); Endian::write_UA_L16(op, uint16_t(offset)); op += 2; } return op; } static inline char* emitCopy(char* op, size_t offset, int len) { // Emit 64 byte copies but make sure to keep at least four bytes reserved while (len >= 68) { op = emitCopyLessThan64(op, offset, 64); len -= 64; } // Emit an extra 60 byte copy if have too much data to fit in one copy if (len > 64) { op = emitCopyLessThan64(op, offset, 60); len -= 60; } // Emit remainder (at least 4 bytes) op = emitCopyLessThan64(op, offset, len); return op; } // The size of a compression block. Note that many parts of the compression // code assumes that kBlockSize <= 65536; in particular, the hash table // can only store 16-bit offsets, and EmitCopy() also assumes the offset // is 65535 bytes or less. static const size_t BLOCK_SIZE = 1 << 16; // Compresses "input" string to the "*op" buffer. // // REQUIRES: "input" is at most "BLOCK_SIZE" bytes long. // REQUIRES: "op" points to an array of memory that is at least // "maxCompressedLength(input.size())" in size. // // Returns an "end" pointer into "op" buffer. // "end - op" is the compressed size of "input". char* compressFragment(const char* input, size_t inputSize, char* op) { static const int HASH_TABLE_BITS = 14; static const int SHIFT = 32 - HASH_TABLE_BITS; uint16_t table[1 << HASH_TABLE_BITS]; // 32KB memset(table, 0, sizeof(table)); // "ip" is the input pointer, and "op" is the output pointer. const char* ip = input; const char* ipEnd = input + inputSize; assert(inputSize <= BLOCK_SIZE); // Bytes in [nextEmit, ip) will be emitted as literal bytes. Or // [nextEmit, ipEnd) after the main loop. const char* nextEmit = ip; static const size_t INPUT_MARGIN_BYTES = 15; if (likely(inputSize >= INPUT_MARGIN_BYTES)) { const char* ipLimit = ipEnd - INPUT_MARGIN_BYTES; uint32_t nextHash = hash(++ip); while (true) { assert(nextEmit < ip); // The body of this loop calls emitLiteral() once and // then emitCopy one or more times. (The exception is // that when we're close to exhausting the input we // goto emitRemainder.) // // In the first iteration of this loop we're just // starting, so there's nothing to copy, so calling // emitLiteral() once is necessary. And we only start // a new iteration when the current iteration has // determined that a call to emitLiteral() will precede // the next call to emitCopy (if any). // // Step 1: Scan forward in the input looking for a // 4-byte-long match. If we get close to exhausting the // input then goto emitRemainder. // // Heuristic match skipping: If 32 bytes are scanned // with no matches found, start looking only at every // other byte. If 32 more bytes are scanned, look at // every third byte, etc.. When a match is found, // immediately go back to looking at every byte. This // is a small loss (~5% performance, ~0.1% density) for // compressible data due to more bookkeeping, but for // non-compressible data (such as JPEG) it's a huge win // since the compressor quickly "realizes" the data is // incompressible and doesn't bother looking for // matches everywhere. // // The "skip" variable keeps track of how many bytes // there are since the last match; dividing it by 32 // (ie. right-shifting by five) gives the number of // bytes to move ahead for each iteration. uint32_t skip = 32; const char* nextIp = ip; const char* candidate; do { ip = nextIp; uint32_t h = nextHash; assert(h == hash(ip)); uint32_t bytesBetweenHashLookups = skip++ >> 5; nextIp = ip + bytesBetweenHashLookups; if (unlikely(nextIp > ipLimit)) { goto emitRemainder; } nextHash = hash(nextIp); candidate = input + table[h]; assert(candidate >= input); assert(candidate < ip); table[h] = ip - input; } while (likely(unalignedLoad32(ip) != unalignedLoad32(candidate))); // Step 2: A 4-byte match has been found. We'll later // see if more than 4 bytes match. But, prior to the // match, input bytes [nextEmit, ip) are unmatched. // Emit them as "literal bytes." assert(nextEmit + 16 <= ipEnd); op = emitLiteral(op, nextEmit, ip - nextEmit); // Step 3: Call emitCopy, and then see if another // emitCopy could be our next move. Repeat until we // find no match for the input immediately after what // was consumed by the last emitCopy call. // // If we exit this loop normally then we need to call // emitLiteral() next, though we don't yet know how big // the literal will be. We handle that by proceeding // to the next iteration of the main loop. We also can // exit this loop via goto if we get close to // exhausting the input. EightBytesReference inputBytes; uint32_t candidateBytes = 0; do { // We have a 4-byte match at ip, and no need to // emit any "literal bytes" prior to ip. int matched = 4 + findMatchLength(candidate + 4, ip + 4, ipEnd); size_t offset = ip - candidate; assert(0 == memcmp(ip, candidate, matched)); ip += matched; op = emitCopy(op, offset, matched); // We could immediately start working at ip // now, but to improve compression we first // update table[hash(ip - 1, ...)]. const char* insertTail = ip - 1; nextEmit = ip; if (unlikely(ip >= ipLimit)) { goto emitRemainder; } inputBytes.setAddress(insertTail); uint32_t prevHash = hashBytes(inputBytes.getUint32AtOffset<0>()); table[prevHash] = ip - input - 1; uint32_t curHash = hashBytes(inputBytes.getUint32AtOffset<1>()); candidate = input + table[curHash]; candidateBytes = unalignedLoad32(candidate); table[curHash] = ip - input; } while (inputBytes.getUint32AtOffset<1>() == candidateBytes); nextHash = hashBytes(inputBytes.getUint32AtOffset<2>()); ++ip; } } emitRemainder: // Emit the remaining bytes as a literal if (nextEmit < ipEnd) { op = emitLiteral(op, nextEmit, ipEnd - nextEmit); } return op; } void compress(const char* input, size_t inLen, char* output, size_t& outLen) { char* out = output; while (inLen > 0) { size_t numToRead = std::min(inLen, BLOCK_SIZE); out = compressFragment(input, numToRead, out); inLen -= numToRead; input += numToRead; } outLen = out - output + SCRATCH_SIZE; } size_t maxCompressedLength(size_t inLen) { return 32 + SCRATCH_SIZE + inLen + inLen / 6; } } openmsx-0.10.0/src/utils/stl.hh0000644000175000017500000000370312262345041017121 0ustar manuelmanuel00000000000000#ifndef STL_HH #define STL_HH #include #include // Dereference the two given (pointer-like) parameters and then compare // them with the less-than operator. struct LessDeref { template bool operator()(PTR p1, PTR p2) const { return *p1 < *p2; } }; // Note: LessTupleElement and CmpTupleElement can be made a lot more general // (and uniform). This can be done relatively easily with variadic templates. // Unfortunately vc++ doesn't support this yet. So for now these classes are // 'just enough' to make the current users of these utilities happy. // Compare the N-th element of a tuple using the less-than operator. Also // provides overloads to compare the N-the element with a single value of the // same type. template struct LessTupleElement { template bool operator()(const TUPLE& x, const TUPLE& y) const { return std::get(x) < std::get(y); } template bool operator()(const typename std::tuple_element::type& x, const TUPLE& y) const { return x < std::get(y); } template bool operator()(const TUPLE& x, const typename std::tuple_element::type& y) const { return std::get(x) < y; } }; // Similar to LessTupleElement, but with a custom comparison functor. ATM the // functor cannot take constructor arguments, possibly refactor this in the // future. template struct CmpTupleElement { template bool operator()(const std::pair& x, const std::pair& y) const { return cmp(std::get(x), std::get(y)); } template bool operator()(const T& x, const std::pair& y) const { return cmp(x, std::get(y)); } template bool operator()(const std::pair& x, const T& y) const { return cmp(std::get(x), y); } private: CMP cmp; }; #endif openmsx-0.10.0/src/utils/HexDump.hh0000644000175000017500000000032112262345041017662 0ustar manuelmanuel00000000000000#ifndef HEXDUMP_HH #define HEXDUMP_HH #include namespace HexDump { std::string encode(const void* input, size_t len, bool newlines = true); std::string decode(const std::string& input); } #endif openmsx-0.10.0/src/utils/SerializeBuffer.cc0000644000175000017500000000551412262345041021370 0ustar manuelmanuel00000000000000#include "SerializeBuffer.hh" #include "likely.hh" #include // for bad_alloc #include namespace openmsx { // class OutputBuffer size_t OutputBuffer::lastSize = 50000; // initial estimate OutputBuffer::OutputBuffer() : begin(static_cast(malloc(lastSize))) , end(begin) , finish(begin + lastSize) { // We've allocated a buffer with an estimated initial size. This // estimate is based on the largest intermediate size of the previously // required buffers. // For correctness this initial size doesn't matter (we could even not // allocate any initial buffer at all). But it can make a difference in // performance. If later we discover the buffer is too small, we have // to reallocate (and thus make a copy). In profiling this reallocation // step was noticable. if (!begin) { throw std::bad_alloc(); } // Slowly drop the estimated required size. This makes sure that when // we've overestimated the size once, we don't forever keep this too // high value. For performance an overestimation is less bad than an // underestimation. lastSize -= lastSize >> 7; } OutputBuffer::~OutputBuffer() { free(begin); } #ifdef __GNUC__ template void OutputBuffer::insertN(const void* __restrict data) __restrict { byte* newEnd = end + LEN; if (likely(newEnd <= finish)) { memcpy(end, data, LEN); end = newEnd; } else { insertGrow(data, LEN); } } // Force template instantiation template void OutputBuffer::insertN<1>(const void* __restrict data) __restrict; template void OutputBuffer::insertN<2>(const void* __restrict data) __restrict; template void OutputBuffer::insertN<4>(const void* __restrict data) __restrict; template void OutputBuffer::insertN<8>(const void* __restrict data) __restrict; #endif void OutputBuffer::insertN(const void* __restrict data, size_t len) __restrict { byte* newEnd = end + len; if (likely(newEnd <= finish)) { memcpy(end, data, len); end = newEnd; } else { insertGrow(data, len); } } byte* OutputBuffer::release(size_t& size) { size = end - begin; // Deallocate unused buffer space. byte* result = static_cast(realloc(begin, size)); begin = end = finish = nullptr; return result; } void OutputBuffer::insertGrow(const void* __restrict data, size_t len) __restrict { byte* pos = allocateGrow(len); memcpy(pos, data, len); } byte* OutputBuffer::allocateGrow(size_t len) __restrict { size_t oldSize = end - begin; size_t newSize = std::max(oldSize + len, oldSize + oldSize / 2); auto newBegin = static_cast(realloc(begin, newSize)); if (!newBegin) { throw std::bad_alloc(); } begin = newBegin; end = begin + oldSize + len; finish = begin + newSize; return begin + oldSize; } // class InputBuffer InputBuffer::InputBuffer(const byte* data, size_t size) : buf(data) #ifndef NDEBUG , finish(buf + size) #endif { (void)size; } } // namespace openmsx openmsx-0.10.0/src/utils/utf8_checked.cc0000644000175000017500000000364212262345041020643 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "utf8_checked.hh" #include "vla.hh" #include "StringOp.hh" #include "MSXException.hh" #include namespace utf8 { bool multibytetoutf16(const std::string& multibyte, UINT cp, DWORD dwFlags, std::wstring& utf16) { const char* multibyteA = multibyte.c_str(); int len = MultiByteToWideChar(cp, dwFlags, multibyteA, -1, nullptr, 0); if (len) { VLA(wchar_t, utf16W, len); len = MultiByteToWideChar(cp, dwFlags, multibyteA, -1, utf16W, len); if (len) { utf16 = utf16W; return true; } } return false; } bool utf16tomultibyte(const std::wstring& utf16, UINT cp, std::string& multibyte) { const wchar_t* utf16W = utf16.c_str(); int len = WideCharToMultiByte(cp, 0, utf16W, -1, nullptr, 0, nullptr, nullptr); if (len) { VLA(char, multibyteA, len); len = WideCharToMultiByte(cp, 0, utf16W, -1, multibyteA, len, nullptr, nullptr); if (len) { multibyte = multibyteA; return true; } } return false; } std::string utf8toansi(const std::string& utf8) { std::wstring utf16; if (!multibytetoutf16(utf8, CP_UTF8, MB_ERR_INVALID_CHARS, utf16)) { throw openmsx::FatalError(StringOp::Builder() << "MultiByteToWideChar failed: " << GetLastError()); } std::string ansi; if (!utf16tomultibyte(utf16, CP_ACP, ansi)) { throw openmsx::FatalError(StringOp::Builder() << "MultiByteToWideChar failed: " << GetLastError()); } return ansi; } std::wstring utf8to16(const std::string& utf8) { std::wstring utf16; if (!multibytetoutf16(utf8, CP_UTF8, MB_ERR_INVALID_CHARS, utf16)) { throw openmsx::FatalError(StringOp::Builder() << "MultiByteToWideChar failed: " << GetLastError()); } return utf16; } std::string utf16to8(const std::wstring& utf16) { std::string utf8; if (!utf16tomultibyte(utf16, CP_UTF8, utf8)) { throw openmsx::FatalError(StringOp::Builder() << "MultiByteToWideChar failed: " << GetLastError()); } return utf8; } } // namespace utf8 #endif // _WIN32 openmsx-0.10.0/src/utils/utf8_checked.hh0000644000175000017500000002202612262345041020652 0ustar manuelmanuel00000000000000// UTF8-CPP http://utfcpp.sourceforge.net/ // Slightly simplified (and reformatted) to fit openMSX coding style. // Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_CHECKED_HH #define UTF8_CHECKED_HH #include "utf8_core.hh" #include namespace utf8 { // Exceptions that may be thrown from the library functions. class invalid_code_point : public std::exception { uint32_t cp; public: explicit invalid_code_point(uint32_t cp_) : cp(cp_) {} virtual const char* what() const throw() { return "Invalid code point"; } uint32_t code_point() const { return cp; } }; class invalid_utf8 : public std::exception { uint8_t u8; public: explicit invalid_utf8(uint8_t u) : u8(u) {} virtual const char* what() const throw() { return "Invalid UTF-8"; } uint8_t utf8_octet() const { return u8; } }; class invalid_utf16 : public std::exception { uint16_t u16; public: explicit invalid_utf16(uint16_t u) : u16(u) {} virtual const char* what() const throw() { return "Invalid UTF-16"; } uint16_t utf16_word() const { return u16; } }; class not_enough_room : public std::exception { public: virtual const char* what() const throw() { return "Not enough space"; } }; // The library API - functions intended to be called by the users template output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) { while (start != end) { auto sequence_start = start; internal::utf_error err_code = internal::validate_next(start, end); switch (err_code) { case internal::OK: for (auto it = sequence_start; it != start; ++it) { *out++ = *it; } break; case internal::NOT_ENOUGH_ROOM: throw not_enough_room(); case internal::INVALID_LEAD: append(replacement, out); ++start; break; case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: case internal::INVALID_CODE_POINT: append(replacement, out); ++start; // just one replacement mark for the sequence while (internal::is_trail(*start) && start != end) { ++start; } break; } } return out; } template inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) { return replace_invalid(start, end, out, 0xfffd); } template octet_iterator append(uint32_t cp, octet_iterator result) { if (!internal::is_code_point_valid(cp)) { throw invalid_code_point(cp); } if (cp < 0x80) { // one octet *result++ = cp; } else if (cp < 0x800) { // two octets *result++ = ((cp >> 6) ) | 0xc0; *result++ = ((cp >> 0) & 0x3f) | 0x80; } else if (cp < 0x10000) { // three octets *result++ = ((cp >> 12) ) | 0xe0; *result++ = ((cp >> 6) & 0x3f) | 0x80; *result++ = ((cp >> 0) & 0x3f) | 0x80; } else if (cp <= internal::CODE_POINT_MAX) { // four octets *result++ = ((cp >> 18) ) | 0xf0; *result++ = ((cp >> 12) & 0x3f) | 0x80; *result++ = ((cp >> 6) & 0x3f) | 0x80; *result++ = ((cp >> 0) & 0x3f) | 0x80; } else { throw invalid_code_point(cp); } return result; } template uint32_t next(octet_iterator& it, octet_iterator end) { uint32_t cp = 0; internal::utf_error err_code = internal::validate_next(it, end, &cp); switch (err_code) { case internal::OK : break; case internal::NOT_ENOUGH_ROOM: throw not_enough_room(); case internal::INVALID_LEAD: case internal::INCOMPLETE_SEQUENCE: case internal::OVERLONG_SEQUENCE: throw invalid_utf8(*it); case internal::INVALID_CODE_POINT: throw invalid_code_point(cp); } return cp; } template uint32_t peek_next(octet_iterator it, octet_iterator end) { return next(it, end); } template uint32_t prior(octet_iterator& it, octet_iterator start) { auto end = it; while (internal::is_trail(*(--it))) { if (it < start) { // error - no lead byte in the sequence throw invalid_utf8(*it); } } auto temp = it; return next(temp, end); } template void advance(octet_iterator& it, distance_type n, octet_iterator end) { for (distance_type i = 0; i < n; ++i) { next(it, end); } } template typename std::iterator_traits::difference_type distance(octet_iterator first, octet_iterator last) { typename std::iterator_traits::difference_type dist; for (dist = 0; first < last; ++dist) { next(first, last); } return dist; } template octet_iterator utf16to8(u16bit_iterator start, u16bit_iterator end, octet_iterator result) { while (start != end) { uint32_t cp = *start++; // Take care of surrogate pairs first if (internal::is_surrogate(cp)) { if (start == end) { throw invalid_utf16(*start); } uint32_t trail_surrogate = *start++; if (trail_surrogate < internal::TRAIL_SURROGATE_MIN || trail_surrogate > internal::TRAIL_SURROGATE_MAX) { throw invalid_utf16(trail_surrogate); } cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; } result = append(cp, result); } return result; } template u16bit_iterator utf8to16(octet_iterator start, octet_iterator end, u16bit_iterator result) { while (start != end) { uint32_t cp = next(start, end); if (cp > 0xffff) { // make a surrogate pair *result++ = (cp >> 10) + internal::LEAD_OFFSET; *result++ = (cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN; } else { *result++ = cp; } } return result; } template octet_iterator utf32to8(u32bit_iterator start, u32bit_iterator end, octet_iterator result) { while (start != end) { result = append(*start++, result); } return result; } template u32bit_iterator utf8to32(octet_iterator start, octet_iterator end, u32bit_iterator result) { while (start < end) { *result++ = next(start, end); } return result; } // The iterator class template class iterator : public std::iterator { octet_iterator it; octet_iterator range_start; octet_iterator range_end; public: iterator() {}; iterator(const octet_iterator& octet_it, const octet_iterator& range_start, const octet_iterator& range_end) : it(octet_it) , range_start(range_start) , range_end(range_end) { if (it < range_start || it > range_end) { throw std::out_of_range("Invalid utf-8 iterator position"); } } // the default "big three" are OK octet_iterator base() const { return it; } uint32_t operator*() const { auto temp = it; return next(temp, range_end); } bool operator==(const iterator& rhs) const { if ((range_start != rhs.range_start) || (range_end != rhs.range_end)) { throw std::logic_error( "Comparing utf-8 iterators defined with different ranges"); } return it == rhs.it; } bool operator!=(const iterator& rhs) const { return !(operator==(rhs)); } iterator& operator++() { next(it, range_end); return *this; } iterator operator++(int) { auto temp = *this; next(it, range_end); return temp; } iterator& operator--() { prior(it, range_start); return *this; } iterator operator--(int) { auto temp = *this; prior(it, range_start); return temp; } }; #ifdef _WIN32 std::string unknowntoutf8(const std::string& unknown); std::string utf8toansi(const std::string& utf8); std::wstring utf8to16(const std::string& utf8); std::string utf16to8(const std::wstring& utf16); #endif } // namespace utf8 #endif openmsx-0.10.0/src/utils/Date.hh0000644000175000017500000000046012262345041017171 0ustar manuelmanuel00000000000000#ifndef DATE_HH #define DATE_HH #include #include namespace openmsx { namespace Date { // 'line' must point to a buffer that is at least 24 characters long time_t fromString(const char* line); std::string toString(time_t time); } // namespace Date } // namespace openmsx #endif openmsx-0.10.0/src/utils/TigerTree.hh0000644000175000017500000000555112262345041020214 0ustar manuelmanuel00000000000000/** Incremental Tiger-tree-hash (tth) calculation. * * This code is based on code from the tigertree project * https://sourceforge.net/projects/tigertree/ * * The main difference is that the original code can only perform a one-shot * tth calculation while this code (also) allows incremental tth calculations. * When the input data changes, incremental calculation allows to only redo * part of the full calculation, so it can be much faster. * * Here's the copyright notice of the original code (even though the code is * almost completely rewritten): * * Copyright (C) 2001 Bitzi (aka Bitcollider) Inc. and Gordon Mohr * Released into the public domain by same; permission is explicitly * granted to copy, modify, and use freely. * * THE WORK IS PROVIDED "AS IS," AND COMES WITH ABSOLUTELY NO WARRANTY, * EXPRESS OR IMPLIED, TO THE EXTENT PERMITTED BY APPLICABLE LAW, * INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE. * * (PD) 2001 The Bitzi Corporation * Please see file COPYING or http://bitzi.com/publicdomain * for more info. */ #ifndef TIGERTREE_HH #define TIGERTREE_HH #include "tiger.hh" #include "MemBuffer.hh" #include namespace openmsx { /** The TigerTree class will query the to-be-hashed data via this abstract * interface. This allows to e.g. fetch the data from a file. */ class TTData { public: /** Return the requested portion of the to-be-hashed data block. * Special requirement: it should be allowed to temporarily overwrite * the byte one position before the returned pointer. */ virtual uint8_t* getData(size_t offset, size_t size) = 0; protected: ~TTData() {} }; /** Calculate a tiger-tree-hash. * Calculation can be done incrementally, so recalculating the hash after a * (small) modification of the input is efficient. */ class TigerTree { public: /** Create TigerTree calculator for the given (abstract) data block * of given size. */ TigerTree(TTData& data, size_t dataSize); /** Calculate the hash value. */ const TigerHash& calcHash(); /** Inform this calculator about changes in the input data. This is * used to (not) skip re-calculations on future calcHash() calls. So * it's crucial this calculator is informed about _all_ changes in * the input. */ void notifyChange(size_t offset, size_t len); private: // functions to navigate in binary tree struct Node { Node(size_t n_, size_t l_) : n(n_), l(l_) {} size_t n; // node number size_t l; // level number }; Node getTop() const; Node getLeaf(size_t block) const; Node getParent(Node node) const; Node getLeftChild(Node node) const; Node getRightChild(Node node) const; const TigerHash& calcHash(Node node); TTData& data; const size_t dataSize; MemBuffer hash; MemBuffer valid; }; } // namespace openmsx #endif openmsx-0.10.0/src/utils/countof.hh0000644000175000017500000000272212262345041017774 0ustar manuelmanuel00000000000000// countof() returns the number of elements in a statically // allocated array. For example: // // int a[10]; // f(a, countof(a)); // f will be called with value 10 as 2nd parameter // // // A naive implementation is this: // // #define countof(array) (sizeof(array) / sizeof(array[0])) // // This implementation has a problem. The following example compiles fine, but // it gives the wrong result: // // int a[10]; // int* p = a; // countof(p); // wrong result when called with pointer argument // // // A better implementation is this: // // template // unsigned countof(T(&)[N]) // { // return N; // } // // The above example now results in a compilation error (which is good). // Though in this implementation, the result of countof() is no longer a // compile-time constant. So now this example fails to compile: // // int a[10]; // int b[countof(a)]; // error: array bound is not a constant // // // The implementation below fixes both problems. #ifndef COUNTOF_HH #define COUNTOF_HH // The following line uses a very obscure syntax: // It declares a templatized function 'countofHelper'. // It has one (unnamed) parameter: a reference to an array T[N]. // The return type is a reference to an array char[N] // Note that this function does not need an implementation. template char (&countofHelper(T(&)[N]))[N]; #define countof(array) (sizeof(countofHelper(array))) #endif openmsx-0.10.0/src/utils/utf8_core.hh0000644000175000017500000001406012262345041020213 0ustar manuelmanuel00000000000000// UTF8-CPP http://utfcpp.sourceforge.net/ // Slightly simplified (and reformatted) to fit openMSX coding style. // Copyright 2006 Nemanja Trifunovic /* Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef UTF8_CORE_HH #define UTF8_CORE_HH #include #include namespace utf8 { // Helper code - not intended to be directly called by the library users. // May be changed at any time namespace internal { // Unicode constants // Leading (high) surrogates: 0xd800 - 0xdbff // Trailing (low) surrogates: 0xdc00 - 0xdfff const uint16_t LEAD_SURROGATE_MIN = 0xd800u; const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; // Maximum valid value for a Unicode code point const uint32_t CODE_POINT_MAX = 0x0010ffffu; inline bool is_trail(uint8_t oc) { return (oc >> 6) == 0x2; } inline bool is_surrogate(uint16_t cp) { return (cp >= LEAD_SURROGATE_MIN) && (cp <= TRAIL_SURROGATE_MAX); } inline bool is_code_point_valid(uint32_t cp) { return (cp <= CODE_POINT_MAX) && !is_surrogate(cp) && (cp != 0xfffe) && (cp != 0xffff); } inline unsigned sequence_length(uint8_t lead) { if (lead < 0x80) { return 1; } else if ((lead >> 5) == 0x06) { return 2; } else if ((lead >> 4) == 0x0e) { return 3; } else if ((lead >> 3) == 0x1e) { return 4; } else { return 0; } } enum utf_error { OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT }; template utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t* code_point) { uint32_t cp = *it; // Check the lead octet int length = sequence_length(*it); // "Shortcut" for ASCII characters if (length == 1) { if (end - it <= 0) { return NOT_ENOUGH_ROOM; } if (code_point) { *code_point = cp; } ++it; return OK; } // Do we have enough memory? if (std::distance(it, end) < length) { return NOT_ENOUGH_ROOM; } // Check trail octets and calculate the code point switch (length) { case 0: return INVALID_LEAD; case 2: if (is_trail(*(++it))) { cp = ((cp << 6) & 0x7ff) + ((*it) & 0x3f); } else { --it; return INCOMPLETE_SEQUENCE; } break; case 3: if (is_trail(*(++it))) { cp = ((cp << 12) & 0xffff) + ((*it << 6) & 0xfff); if (is_trail(*(++it))) { cp += (*it) & 0x3f; } else { std::advance(it, -2); return INCOMPLETE_SEQUENCE; } } else { --it; return INCOMPLETE_SEQUENCE; } break; case 4: if (is_trail(*(++it))) { cp = ((cp << 18) & 0x1fffff) + ((*it << 12) & 0x3ffff); if (is_trail(*(++it))) { cp += (*it << 6) & 0xfff; if (is_trail(*(++it))) { cp += (*it) & 0x3f; } else { std::advance(it, -3); return INCOMPLETE_SEQUENCE; } } else { std::advance(it, -2); return INCOMPLETE_SEQUENCE; } } else { --it; return INCOMPLETE_SEQUENCE; } break; } // Is the code point valid? if (!is_code_point_valid(cp)) { for (int i = 0; i < length - 1; ++i) { --it; } return INVALID_CODE_POINT; } if (code_point) { *code_point = cp; } if (cp < 0x80) { if (length != 1) { std::advance(it, -(length-1)); return OVERLONG_SEQUENCE; } } else if (cp < 0x800) { if (length != 2) { std::advance(it, -(length-1)); return OVERLONG_SEQUENCE; } } else if (cp < 0x10000) { if (length != 3) { std::advance(it, -(length-1)); return OVERLONG_SEQUENCE; } } ++it; return OK; } template inline utf_error validate_next(octet_iterator& it, octet_iterator end) { return validate_next(it, end, nullptr); } } // namespace internal /// The library API - functions intended to be called by the users // Byte order mark const uint8_t bom[] = { 0xef, 0xbb, 0xbf }; template octet_iterator find_invalid(octet_iterator start, octet_iterator end) { auto result = start; while (result != end) { internal::utf_error err_code = internal::validate_next(result, end); if (err_code != internal::OK) { return result; } } return result; } template inline bool is_valid(octet_iterator start, octet_iterator end) { return find_invalid(start, end) == end; } template inline bool is_bom(octet_iterator it) { return ((*it++ == bom[0]) && (*it++ == bom[1]) && (*it == bom[2])); } template inline octet_iterator sync_forward(octet_iterator it) { while (internal::is_trail(*it)) ++it; return it; } template inline octet_iterator sync_backward(octet_iterator it) { while (internal::is_trail(*it)) --it; return it; } } // namespace utf8 #endif openmsx-0.10.0/src/utils/sha1.cc0000644000175000017500000002065612262345041017147 0ustar manuelmanuel00000000000000/* Based on: 100% free public domain implementation of the SHA-1 algorithm by Dominik Reichl Refactored in C++ style as part of openMSX by Maarten ter Huurne and Wouter Vermaelen. === Test Vectors (from FIPS PUB 180-1) === "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #include "sha1.hh" #include "MSXException.hh" #include "CliComm.hh" #include "EventDistributor.hh" #include "StringOp.hh" #include "endian.hh" #include #include #include #include using std::string; namespace openmsx { // Rotate x bits to the left inline static uint32_t rol32(uint32_t value, int bits) { return (value << bits) | (value >> (32 - bits)); } class WorkspaceBlock { private: uint32_t data[16]; uint32_t next0(int i) { data[i] = Endian::readB32(&data[i]); return data[i]; } uint32_t next(int i) { return data[i & 15] = rol32( data[(i + 13) & 15] ^ data[(i + 8) & 15] ^ data[(i + 2) & 15] ^ data[ i & 15] , 1); } public: explicit WorkspaceBlock(const uint8_t buffer[64]); // SHA-1 rounds void r0(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) { z += ((w & (x ^ y)) ^ y) + next0(i) + 0x5A827999 + rol32(v, 5); w = rol32(w, 30); } void r1(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) { z += ((w & (x ^ y)) ^ y) + next(i) + 0x5A827999 + rol32(v, 5); w = rol32(w, 30); } void r2(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) { z += (w ^ x ^ y) + next(i) + 0x6ED9EBA1 + rol32(v, 5); w = rol32(w, 30); } void r3(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) { z += (((w | x) & y) | (w & x)) + next(i) + 0x8F1BBCDC + rol32(v, 5); w = rol32(w, 30); } void r4(uint32_t v, uint32_t& w, uint32_t x, uint32_t y, uint32_t& z, int i) { z += (w ^ x ^ y) + next(i) + 0xCA62C1D6 + rol32(v, 5); w = rol32(w, 30); } }; WorkspaceBlock::WorkspaceBlock(const uint8_t buffer[64]) { memcpy(data, buffer, sizeof(data)); } // class Sha1Sum Sha1Sum::Sha1Sum() { clear(); } Sha1Sum::Sha1Sum(string_ref str) { if (str.size() != 40) { throw MSXException("Invalid sha1, should be exactly 40 digits long: " + str); } parse40(str.data()); } static inline unsigned hex(char x, const char* str) { if (('0' <= x) && (x <= '9')) return x - '0'; if (('a' <= x) && (x <= 'f')) return x - 'a' + 10; if (('A' <= x) && (x <= 'F')) return x - 'A' + 10; throw MSXException("Invalid sha1, digits should be 0-9, a-f: " + string(str, 40)); } void Sha1Sum::parse40(const char* str) { const char* p = str; for (int i = 0; i < 5; ++i) { unsigned t = 0; for (int j = 0; j < 8; ++j) { t <<= 4; t |= hex(*p++, str); } a[i] = t; } } static inline char digit(unsigned x) { return (x < 10) ? (x + '0') : (x - 10 + 'a'); } std::string Sha1Sum::toString() const { char buf[40]; char* p = buf; for (int i = 0; i < 5; ++i) { for (int j = 28; j >= 0; j -= 4) { *p++ = digit((a[i] >> j) & 0xf); } } return string(buf, 40); } bool Sha1Sum::empty() const { for (int i = 0; i < 5; ++i) { if (a[i] != 0) return false; } return true; } void Sha1Sum::clear() { for (int i = 0; i < 5; ++i) { a[i] = 0; } } bool Sha1Sum::operator==(const Sha1Sum& other) const { for (int i = 0; i < 5; ++i) { if (a[i] != other.a[i]) return false; } return true; } bool Sha1Sum::operator!=(const Sha1Sum& other) const { return !(*this == other); } bool Sha1Sum::operator<(const Sha1Sum& other) const { for (int i = 0; i < 5-1; ++i) { if (a[i] != other.a[i]) return a[i] < other.a[i]; } return a[5-1] < other.a[5-1]; } // class SHA1 SHA1::SHA1() { // SHA1 initialization constants m_state.a[0] = 0x67452301; m_state.a[1] = 0xEFCDAB89; m_state.a[2] = 0x98BADCFE; m_state.a[3] = 0x10325476; m_state.a[4] = 0xC3D2E1F0; m_count = 0; m_finalized = false; } void SHA1::transform(const uint8_t buffer[64]) { WorkspaceBlock block(buffer); // Copy m_state[] to working vars uint32_t a = m_state.a[0]; uint32_t b = m_state.a[1]; uint32_t c = m_state.a[2]; uint32_t d = m_state.a[3]; uint32_t e = m_state.a[4]; // 4 rounds of 20 operations each. Loop unrolled block.r0(a,b,c,d,e, 0); block.r0(e,a,b,c,d, 1); block.r0(d,e,a,b,c, 2); block.r0(c,d,e,a,b, 3); block.r0(b,c,d,e,a, 4); block.r0(a,b,c,d,e, 5); block.r0(e,a,b,c,d, 6); block.r0(d,e,a,b,c, 7); block.r0(c,d,e,a,b, 8); block.r0(b,c,d,e,a, 9); block.r0(a,b,c,d,e,10); block.r0(e,a,b,c,d,11); block.r0(d,e,a,b,c,12); block.r0(c,d,e,a,b,13); block.r0(b,c,d,e,a,14); block.r0(a,b,c,d,e,15); block.r1(e,a,b,c,d,16); block.r1(d,e,a,b,c,17); block.r1(c,d,e,a,b,18); block.r1(b,c,d,e,a,19); block.r2(a,b,c,d,e,20); block.r2(e,a,b,c,d,21); block.r2(d,e,a,b,c,22); block.r2(c,d,e,a,b,23); block.r2(b,c,d,e,a,24); block.r2(a,b,c,d,e,25); block.r2(e,a,b,c,d,26); block.r2(d,e,a,b,c,27); block.r2(c,d,e,a,b,28); block.r2(b,c,d,e,a,29); block.r2(a,b,c,d,e,30); block.r2(e,a,b,c,d,31); block.r2(d,e,a,b,c,32); block.r2(c,d,e,a,b,33); block.r2(b,c,d,e,a,34); block.r2(a,b,c,d,e,35); block.r2(e,a,b,c,d,36); block.r2(d,e,a,b,c,37); block.r2(c,d,e,a,b,38); block.r2(b,c,d,e,a,39); block.r3(a,b,c,d,e,40); block.r3(e,a,b,c,d,41); block.r3(d,e,a,b,c,42); block.r3(c,d,e,a,b,43); block.r3(b,c,d,e,a,44); block.r3(a,b,c,d,e,45); block.r3(e,a,b,c,d,46); block.r3(d,e,a,b,c,47); block.r3(c,d,e,a,b,48); block.r3(b,c,d,e,a,49); block.r3(a,b,c,d,e,50); block.r3(e,a,b,c,d,51); block.r3(d,e,a,b,c,52); block.r3(c,d,e,a,b,53); block.r3(b,c,d,e,a,54); block.r3(a,b,c,d,e,55); block.r3(e,a,b,c,d,56); block.r3(d,e,a,b,c,57); block.r3(c,d,e,a,b,58); block.r3(b,c,d,e,a,59); block.r4(a,b,c,d,e,60); block.r4(e,a,b,c,d,61); block.r4(d,e,a,b,c,62); block.r4(c,d,e,a,b,63); block.r4(b,c,d,e,a,64); block.r4(a,b,c,d,e,65); block.r4(e,a,b,c,d,66); block.r4(d,e,a,b,c,67); block.r4(c,d,e,a,b,68); block.r4(b,c,d,e,a,69); block.r4(a,b,c,d,e,70); block.r4(e,a,b,c,d,71); block.r4(d,e,a,b,c,72); block.r4(c,d,e,a,b,73); block.r4(b,c,d,e,a,74); block.r4(a,b,c,d,e,75); block.r4(e,a,b,c,d,76); block.r4(d,e,a,b,c,77); block.r4(c,d,e,a,b,78); block.r4(b,c,d,e,a,79); // Add the working vars back into m_state[] m_state.a[0] += a; m_state.a[1] += b; m_state.a[2] += c; m_state.a[3] += d; m_state.a[4] += e; } // Use this function to hash in binary data and strings void SHA1::update(const uint8_t* data, size_t len) { assert(!m_finalized); uint32_t j = (m_count >> 3) & 63; m_count += uint64_t(len) << 3; uint32_t i; if ((j + len) > 63) { memcpy(&m_buffer[j], data, (i = 64 - j)); transform(m_buffer); for (; i + 63 < len; i += 64) { transform(&data[i]); } j = 0; } else { i = 0; } memcpy(&m_buffer[j], &data[i], len - i); } void SHA1::finalize() { assert(!m_finalized); uint8_t finalcount[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; for (int i = 0; i < 8; i++) { finalcount[i] = uint8_t(m_count >> ((7 - i) * 8)); } update(reinterpret_cast("\200"), 1); while ((m_count & 504) != 448) { update(reinterpret_cast("\0"), 1); } update(finalcount, 8); // cause a transform() m_finalized = true; } Sha1Sum SHA1::digest() { if (!m_finalized) finalize(); return m_state; } Sha1Sum SHA1::calc(const uint8_t* data, size_t len) { SHA1 sha1; sha1.update(data, len); return sha1.digest(); } static void reportProgress(const string& filename, size_t percentage, CliComm& cliComm, EventDistributor& distributor) { cliComm.printProgress("Calculating SHA1 sum for " + filename + "... " + StringOp::toString(percentage) + "%"); distributor.deliverEvents(); } Sha1Sum SHA1::calcWithProgress(const uint8_t* data, size_t len, const string& filename, CliComm& cliComm, EventDistributor& distributor) { if (len < 10*1024*1024) { // for small files, don't show progress return calc(data, len); } SHA1 sha1; static const size_t NUMBER_OF_STEPS = 100; // calculate in NUMBER_OF_STEPS steps and report progress every step auto stepSize = len / NUMBER_OF_STEPS; auto remainder = len % NUMBER_OF_STEPS; size_t offset = 0; reportProgress(filename, 0, cliComm, distributor); for (size_t i = 0; i < (NUMBER_OF_STEPS - 1); ++i) { sha1.update(&data[offset], stepSize); offset += stepSize; reportProgress(filename, i + 1, cliComm, distributor); } sha1.update(data + offset, stepSize + remainder); reportProgress(filename, 100, cliComm, distributor); return sha1.digest(); } } // namespace openmsx openmsx-0.10.0/src/utils/endian.hh0000644000175000017500000003034312262345041017555 0ustar manuelmanuel00000000000000#ifndef ENDIAN_HH #define ENDIAN_HH #include "alignof.hh" #include "build-info.hh" #include #include namespace Endian { // Revese bytes in a 16-bit number: 0x1234 becomes 0x3412 static inline uint16_t bswap16(uint16_t x) { // This sequence generates 'optimal' code on a wide range of gcc/clang // versions (a single rotate instruction on x86). The newer compiler // versions also do 'the right thing' for the simpler expression below. // Those newer compilers also support __builtin_bswap16() but that // doesn't generate better code (and is less portable). return ((x & 0x00FF) << 8) | ((x & 0xFF00) >> 8); //return (x << 8) | (x >> 8); } // Revese bytes in a 32-bit number: 0x12345678 becomes 0x78563412 static inline uint32_t bswap32(uint32_t x) { #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) // Starting from gcc-4.3 there's a builtin function for this. // E.g. on x86 this is translated to a single 'bswap' instruction. return __builtin_bswap32(x); #else return (x << 24) | ((x << 8) & 0x00ff0000) | ((x >> 8) & 0x0000ff00) | (x >> 24); #endif } // Revese bytes in a 64-bit value: 0x1122334455667788 becomes 0x8877665544332211 static inline uint32_t bswap64(uint64_t x) { #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3)) // Starting from gcc-4.3 there's a builtin function for this. // E.g. on x86 this is translated to a single 'bswap' instruction. return __builtin_bswap64(x); #else return (uint64_t(bswap32(x >> 0)) << 32) | (uint64_t(bswap32(x >> 32)) << 0); #endif } // Use overloading to get a (statically) polymorphic bswap() function. static inline uint16_t bswap(uint16_t x) { return bswap16(x); } static inline uint32_t bswap(uint32_t x) { return bswap32(x); } static inline uint64_t bswap(uint64_t x) { return bswap64(x); } // Identity operator, simply returns the given value. struct Ident { template inline T operator()(T t) const { return t; } }; // Byte-swap operator, swap bytes in the given value (16 or 32 bit). struct BSwap { template inline T operator()(T t) const { return bswap(t); } }; // Helper class that stores a value and allows to read/write that value. Though // right before it is loaded/stored the value is transformed by a configurable // operation. // TODO If needed this can be extended with stuff like operator+= .... template class EndianT { public: // TODO These constructors are useful, but they prevent this type from // being used in a union. This limitation is removed in C++11. //EndianT() { /* leave uninitialized */ } //EndianT(T t_) { Op op; t = op(t_); } inline operator T() const { Op op; return op(t); } inline EndianT& operator=(T a) { Op op; t = op(a); return *this; } private: T t; }; // Define the types B16, B32, L16, L32. // // Typically these types are used to define the layout of external structures // For example: // // struct FATDirectoryEntry { // char filename[8]; // char extension[3]; // ... // Endian::L32 size; // 32-bit little endian value // }; // ... // unsigned s = myDirEntry.size; // Possibly performs endianess conversion. // yourDirEntry.size = s; // If native endianess is already correct // // this has no extra overhead. // // You can assign and read values in native endianess to values of these types. // So basically in a single location define the structure with the correct // endianess and in all other places use the value as-if it were a native type. // // Note that these types should still be correctly aligned (e.g. L32 should be // 4-byte aligned). For unaligned access use the functions below. // template struct ConvBig; template<> struct ConvBig : Ident {}; template<> struct ConvBig : BSwap {}; template struct ConvLittle; template<> struct ConvLittle : BSwap {}; template<> struct ConvLittle : Ident {}; typedef EndianT> B16; typedef EndianT> L16; typedef EndianT> B32; typedef EndianT> L32; static_assert(sizeof(B16) == 2, "must have size 2"); static_assert(sizeof(L16) == 2, "must have size 2"); static_assert(sizeof(B32) == 4, "must have size 4"); static_assert(sizeof(L32) == 4, "must have size 4"); static_assert(ALIGNOF(B16) <= 2, "may have alignment 2"); static_assert(ALIGNOF(L16) <= 2, "may have alignment 2"); static_assert(ALIGNOF(B32) <= 4, "may have alignment 4"); static_assert(ALIGNOF(L32) <= 4, "may have alignment 4"); // Helper functions to read/write aligned 16/32 bit values. static inline void writeB16(void* p, uint16_t x) { *reinterpret_cast(p) = x; } static inline void writeL16(void* p, uint16_t x) { *reinterpret_cast(p) = x; } static inline void writeB32(void* p, uint32_t x) { *reinterpret_cast(p) = x; } static inline void writeL32(void* p, uint32_t x) { *reinterpret_cast(p) = x; } static inline uint16_t readB16(const void* p) { return *reinterpret_cast(p); } static inline uint16_t readL16(const void* p) { return *reinterpret_cast(p); } static inline uint32_t readB32(const void* p) { return *reinterpret_cast(p); } static inline uint32_t readL32(const void* p) { return *reinterpret_cast(p); } // Read/write big/little 16/32/64-bit values to/from a (possibly) unaligned // memory location. If the host architecture supports unaligned load/stores // (e.g. x86), these functions perform a single load/store (with possibly an // adjust operation on the value if the endianess is different from the host // endianess). If the architecture does not support unaligned memory operations // (e.g. early ARM architectures), the operation is split into byte accesses. template static inline void write_UA(void* p, T x) { if (SWAP) x = bswap(x); memcpy(p, &x, sizeof(x)); } static inline void write_UA_B16(void* p, uint16_t x) { write_UA(p, x); } static inline void write_UA_L16(void* p, uint16_t x) { write_UA< openmsx::OPENMSX_BIGENDIAN>(p, x); } static inline void write_UA_B32(void* p, uint32_t x) { write_UA(p, x); } static inline void write_UA_L32(void* p, uint32_t x) { write_UA< openmsx::OPENMSX_BIGENDIAN>(p, x); } static inline void write_UA_B64(void* p, uint64_t x) { write_UA(p, x); } static inline void write_UA_L64(void* p, uint64_t x) { write_UA< openmsx::OPENMSX_BIGENDIAN>(p, x); } template static inline T read_UA(const void* p) { T x; memcpy(&x, p, sizeof(x)); if (SWAP) x = bswap(x); return x; } static inline uint16_t read_UA_B16(const void* p) { return read_UA(p); } static inline uint16_t read_UA_L16(const void* p) { return read_UA< openmsx::OPENMSX_BIGENDIAN, uint16_t>(p); } static inline uint32_t read_UA_B32(const void* p) { return read_UA(p); } static inline uint32_t read_UA_L32(const void* p) { return read_UA< openmsx::OPENMSX_BIGENDIAN, uint32_t>(p); } static inline uint64_t read_UA_B64(const void* p) { return read_UA(p); } static inline uint64_t read_UA_L64(const void* p) { return read_UA< openmsx::OPENMSX_BIGENDIAN, uint64_t>(p); } // Like the types above, but these don't need to be aligned. class UA_B16 { public: inline operator uint16_t() const { return read_UA_B16(x); } inline UA_B16& operator=(uint16_t a) { write_UA_B16(x, a); return *this; } private: uint8_t x[2]; }; class UA_L16 { public: inline operator uint16_t() const { return read_UA_L16(x); } inline UA_L16& operator=(uint16_t a) { write_UA_L16(x, a); return *this; } private: uint8_t x[2]; }; class UA_B32 { public: inline operator uint32_t() const { return read_UA_B32(x); } inline UA_B32& operator=(uint32_t a) { write_UA_B32(x, a); return *this; } private: uint8_t x[4]; }; class UA_L32 { public: inline operator uint32_t() const { return read_UA_L32(x); } inline UA_L32& operator=(uint32_t a) { write_UA_L32(x, a); return *this; } private: uint8_t x[4]; }; static_assert(sizeof(UA_B16) == 2, "must have size 2"); static_assert(sizeof(UA_L16) == 2, "must have size 2"); static_assert(sizeof(UA_B32) == 4, "must have size 4"); static_assert(sizeof(UA_L32) == 4, "must have size 4"); static_assert(ALIGNOF(UA_B16) == 1, "must have alignment 1"); static_assert(ALIGNOF(UA_L16) == 1, "must have alignment 1"); static_assert(ALIGNOF(UA_B32) == 1, "must have alignment 1"); static_assert(ALIGNOF(UA_L32) == 1, "must have alignment 1"); // Template meta-programming. // Get a type of the same size of the given type that stores the value in a // specific endianess. Typically used in template functions that can work on // either 16 or 32 bit values. // usage: // typedef typename Endian::Little::type LE_T; // The type LE_T is now a type that stores values of the same size as 'T' // in little endian format (independent of host endianess). template struct Little; template<> struct Little { typedef uint8_t type; }; template<> struct Little { typedef L16 type; }; template<> struct Little { typedef L32 type; }; template struct Big; template<> struct Big { typedef uint8_t type; }; template<> struct Big { typedef B16 type; }; template<> struct Big { typedef B32 type; }; } // namespace Endian #endif #if 0 /////////////////////////////////////////// // Small functions to inspect code quality using namespace Endian; uint16_t testSwap16(uint16_t x) { return bswap16(x); } uint16_t testSwap16() { return bswap16(0x1234); } uint32_t testSwap32(uint32_t x) { return bswap32(x); } uint32_t testSwap32() { return bswap32(0x12345678); } union T16 { B16 be; L16 le; }; void test1(T16& t, uint16_t x) { t.le = x; } void test2(T16& t, uint16_t x) { t.be = x; } uint16_t test3(T16& t) { return t.le; } uint16_t test4(T16& t) { return t.be; } void testA(uint16_t& s, uint16_t x) { write_UA_L16(&s, x); } void testB(uint16_t& s, uint16_t x) { write_UA_B16(&s, x); } uint16_t testC(uint16_t& s) { return read_UA_L16(&s); } uint16_t testD(uint16_t& s) { return read_UA_B16(&s); } union T32 { B32 be; L32 le; }; void test1(T32& t, uint32_t x) { t.le = x; } void test2(T32& t, uint32_t x) { t.be = x; } uint32_t test3(T32& t) { return t.le; } uint32_t test4(T32& t) { return t.be; } void testA(uint32_t& s, uint32_t x) { write_UA_L32(&s, x); } void testB(uint32_t& s, uint32_t x) { write_UA_B32(&s, x); } uint32_t testC(uint32_t& s) { return read_UA_L32(&s); } uint32_t testD(uint32_t& s) { return read_UA_B32(&s); } /////////////////////////////// // Simple unit test #include int main() { T16 t16; assert(sizeof(t16) == 2); t16.le = 0x1234; assert(t16.le == 0x1234); assert(t16.be == 0x3412); assert(read_UA_L16(&t16) == 0x1234); assert(read_UA_B16(&t16) == 0x3412); t16.be = 0x1234; assert(t16.le == 0x3412); assert(t16.be == 0x1234); assert(read_UA_L16(&t16) == 0x3412); assert(read_UA_B16(&t16) == 0x1234); write_UA_L16(&t16, 0xaabb); assert(t16.le == 0xaabb); assert(t16.be == 0xbbaa); assert(read_UA_L16(&t16) == 0xaabb); assert(read_UA_B16(&t16) == 0xbbaa); write_UA_B16(&t16, 0xaabb); assert(t16.le == 0xbbaa); assert(t16.be == 0xaabb); assert(read_UA_L16(&t16) == 0xbbaa); assert(read_UA_B16(&t16) == 0xaabb); T32 t32; assert(sizeof(t32) == 4); t32.le = 0x12345678; assert(t32.le == 0x12345678); assert(t32.be == 0x78563412); assert(read_UA_L32(&t32) == 0x12345678); assert(read_UA_B32(&t32) == 0x78563412); t32.be = 0x12345678; assert(t32.le == 0x78563412); assert(t32.be == 0x12345678); assert(read_UA_L32(&t32) == 0x78563412); assert(read_UA_B32(&t32) == 0x12345678); write_UA_L32(&t32, 0xaabbccdd); assert(t32.le == 0xaabbccdd); assert(t32.be == 0xddccbbaa); assert(read_UA_L32(&t32) == 0xaabbccdd); assert(read_UA_B32(&t32) == 0xddccbbaa); write_UA_B32(&t32, 0xaabbccdd); assert(t32.le == 0xddccbbaa); assert(t32.be == 0xaabbccdd); assert(read_UA_L32(&t32) == 0xddccbbaa); assert(read_UA_B32(&t32) == 0xaabbccdd); } #endif openmsx-0.10.0/src/utils/vla.hh0000644000175000017500000000304312262345041017076 0ustar manuelmanuel00000000000000#ifndef VLA_HH #define VLA_HH // VLAs (Variable Length Array's) are part of C99, but not of C++98. // Though gcc does support VLAs as an extension. For VC++ we have to emulate // them via alloca. #ifndef _MSC_VER #define VLA(TYPE, NAME, LENGTH) \ TYPE NAME[(LENGTH)] #if defined __i386 || defined __x86_64 #define VLA_ALIGNED(TYPE, NAME, LENGTH, ALIGNMENT) \ TYPE NAME[(LENGTH)] __attribute__((__aligned__((ALIGNMENT)))) #else // Except on x86/x86-64, GCC can align VLAs within a stack frame, but it makes // no guarantees if the requested alignment is larger than the alignment of the // stack frame itself. // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16660 #define VLA_ALIGNED(TYPE, NAME, LENGTH, ALIGNMENT) \ UNABLE_TO_GUARANTEE_VLA_ALIGNMENT_ON_THIS_ARCHITECTURE #endif #else #define VLA(TYPE, NAME, LENGTH) \ auto NAME = static_cast(_alloca(sizeof(TYPE) * (LENGTH))) // mfeingol: evil hack alert #define VLA_ALIGNED(TYPE, NAME, LENGTH, ALIGNMENT) \ size_t cbAlign##NAME = (ALIGNMENT); \ void* palloc##NAME = _alloca(sizeof(TYPE) * (LENGTH) + cbAlign##NAME); \ palloc##NAME = (void*)((size_t(palloc##NAME) + cbAlign##NAME - 1UL) & ~(cbAlign##NAME - 1UL)); \ auto NAME = static_cast(palloc##NAME); \ #endif // Macro to align a buffer that might be used by SSE instructions. // On platforms without SSE no (extra) alignment is performed. #ifdef __SSE2__ #define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH) VLA_ALIGNED(TYPE, NAME, LENGTH, 16) #else #define VLA_SSE_ALIGNED(TYPE, NAME, LENGTH) VLA(TYPE, NAME, LENGTH) #endif #endif // VLA_HH openmsx-0.10.0/src/utils/FixedPoint.hh0000644000175000017500000001470512262345041020374 0ustar manuelmanuel00000000000000#ifndef FIXEDPOINT_HH #define FIXEDPOINT_HH #include "Math.hh" // VC++ needs the lrint() compatibilty functions #include namespace openmsx { /** A fixed point number, implemented by a 32-bit signed integer. * The FRACTION_BITS template argument selects the position of the "binary * point" (base 2 equivalent to decimal point). */ template class FixedPoint { public: /** Number of fractional bits (export template parameter as a constant * so that external code can use it more easily). */ static const unsigned FRACTION_BITS = FRACTION_BITS_; private: /** Fixed point representation of 1. */ static const int ONE = 1 << FRACTION_BITS; /** Precalculated float value of 1 / ONE. */ static const float INV_ONE_F; /** Precalculated double value of 1 / ONE. */ static const double INV_ONE_D; /** Bitmask to filter out the fractional part of a fixed point * representation. */ static const int FRACTION_MASK = ONE - 1; public: /** Create new fixed point object from given representation. * Used by the overloaded operators. * @param value the internal representation. */ static inline FixedPoint create(const int value) { FixedPoint ret; ret.value = value; return ret; } /** Creates an uninitialized fixed point object. * This must be public to allow arrays of FixedPoint objects. */ explicit FixedPoint() {} // Conversion to fixed point: explicit FixedPoint(const int i) : value(i << FRACTION_BITS) {} explicit FixedPoint(const unsigned i) : value(i << FRACTION_BITS) {} explicit FixedPoint(const float f) : value(lrintf(f * ONE)) {} explicit FixedPoint(const double d) : value(lrint(d * ONE)) {} static FixedPoint roundRatioDown(unsigned n, unsigned d) { return create((static_cast(n) << FRACTION_BITS) / d); } static inline int shiftHelper(int x, int s) { return (s >= 0) ? (x >> s) : (x << -s); } template explicit FixedPoint(FixedPoint other) : value(shiftHelper(other.getRawValue(), BITS2 - FRACTION_BITS)) {} // Conversion from fixed point: /** * Returns the integer part (rounded down) of this fixed point number. * Note that for negative numbers, rounding occurs away from zero. */ int toInt() const { return value >> FRACTION_BITS; } /** * Returns the float value that corresponds to this fixed point number. */ float toFloat() const { return value * INV_ONE_F; } /** * Returns the double value that corresponds to this fixed point number. */ double toDouble() const { return value * INV_ONE_D; } /** * Returns the fractional part of this fixed point number as a float. * The fractional part is never negative, even for negative fixed point * numbers. * x.toInt() + x.fractionAsFloat() is approximately equal to x.toFloat() */ float fractionAsFloat() const { return (value & FRACTION_MASK) * INV_ONE_F; } /** * Returns the fractional part of this fixed point number as a double. * The fractional part is never negative, even for negative fixed point * numbers. * x.toInt() + x.fractionAsDouble() is approximately equal to x.toDouble() */ double fractionAsDouble() const { return (value & FRACTION_MASK) * INV_ONE_D; } // Various arithmetic: /** * Returns the result of a division between this fixed point number and * another, rounded towards zero. */ int divAsInt(const FixedPoint other) const { return value / other.value; } /** * Returns this value rounded down. * The result is equal to FixedPoint(fp.toInt()). */ FixedPoint floor() const { return create(value & ~FRACTION_MASK); } /** * Returns the fractional part of this value. * The result is equal to fp - floor(fp). */ FixedPoint fract() const { return create(value & FRACTION_MASK); } /** * Returns the fractional part of this value as an integer. * The result is equal to (fract() * (1 << FRACTION_BITS)).toInt() */ unsigned fractAsInt() const { return value & FRACTION_MASK; } // Arithmetic operators: FixedPoint operator+(const FixedPoint other) const { return create(value + other.value); } FixedPoint operator-(const FixedPoint other) const { return create(value - other.value); } FixedPoint operator*(const FixedPoint other) const { return create(int( (static_cast(value) * other.value) >> FRACTION_BITS)); } FixedPoint operator*(const int i) const { return create(value * i); } /** * Divides two fixed point numbers. * The fractional part is rounded down. */ FixedPoint operator/(const FixedPoint other) const { return create(int( (static_cast(value) << FRACTION_BITS) / other.value)); } FixedPoint operator/(const int i) const { return create(value / i); } FixedPoint operator<<(const int b) const { return create(value << b); } FixedPoint operator>>(const int b) const { return create(value >> b); } // Comparison operators: bool operator==(const FixedPoint other) const { return value == other.value; } bool operator!=(const FixedPoint other) const { return value != other.value; } bool operator<(const FixedPoint other) const { return value < other.value; } bool operator<=(const FixedPoint other) const { return value <= other.value; } bool operator>(const FixedPoint other) const { return value > other.value; } bool operator>=(const FixedPoint other) const { return value >= other.value; } // Arithmetic operators that modify this object: void operator+=(const FixedPoint other) { value += other.value; } void operator-=(const FixedPoint other) { value -= other.value; } /** Increase this value with the smallest possible amount. Typically * used to implement counters at the resolution of this datatype. */ void addQuantum() { value += 1; } // Should only be used by other instances of this class // templatized friend declarations are not possible in c++ int getRawValue() const { return value; } template void serialize(Archive& ar, unsigned /*version*/) { ar.serialize("value", value); } private: int value; }; // Force all constants being defined, some compilers need this: template const int FixedPoint::ONE; template const float FixedPoint::INV_ONE_F = 1.0f / ONE; template const double FixedPoint::INV_ONE_D = 1.0 / ONE; template const int FixedPoint::FRACTION_MASK; } // namespace openmsx #endif // FIXEDPOINT_HH openmsx-0.10.0/src/utils/CRC16.cc0000644000175000017500000004325412262345041017070 0ustar manuelmanuel00000000000000#include "CRC16.hh" namespace openmsx { // Accelerator table to compute the CRC (upto) 64 bits at a time // (total table size is 4kB) const uint16_t CRC16::tab[8][256] = { { 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0, }, { 0x0000, 0x3331, 0x6662, 0x5553, 0xCCC4, 0xFFF5, 0xAAA6, 0x9997, 0x89A9, 0xBA98, 0xEFCB, 0xDCFA, 0x456D, 0x765C, 0x230F, 0x103E, 0x0373, 0x3042, 0x6511, 0x5620, 0xCFB7, 0xFC86, 0xA9D5, 0x9AE4, 0x8ADA, 0xB9EB, 0xECB8, 0xDF89, 0x461E, 0x752F, 0x207C, 0x134D, 0x06E6, 0x35D7, 0x6084, 0x53B5, 0xCA22, 0xF913, 0xAC40, 0x9F71, 0x8F4F, 0xBC7E, 0xE92D, 0xDA1C, 0x438B, 0x70BA, 0x25E9, 0x16D8, 0x0595, 0x36A4, 0x63F7, 0x50C6, 0xC951, 0xFA60, 0xAF33, 0x9C02, 0x8C3C, 0xBF0D, 0xEA5E, 0xD96F, 0x40F8, 0x73C9, 0x269A, 0x15AB, 0x0DCC, 0x3EFD, 0x6BAE, 0x589F, 0xC108, 0xF239, 0xA76A, 0x945B, 0x8465, 0xB754, 0xE207, 0xD136, 0x48A1, 0x7B90, 0x2EC3, 0x1DF2, 0x0EBF, 0x3D8E, 0x68DD, 0x5BEC, 0xC27B, 0xF14A, 0xA419, 0x9728, 0x8716, 0xB427, 0xE174, 0xD245, 0x4BD2, 0x78E3, 0x2DB0, 0x1E81, 0x0B2A, 0x381B, 0x6D48, 0x5E79, 0xC7EE, 0xF4DF, 0xA18C, 0x92BD, 0x8283, 0xB1B2, 0xE4E1, 0xD7D0, 0x4E47, 0x7D76, 0x2825, 0x1B14, 0x0859, 0x3B68, 0x6E3B, 0x5D0A, 0xC49D, 0xF7AC, 0xA2FF, 0x91CE, 0x81F0, 0xB2C1, 0xE792, 0xD4A3, 0x4D34, 0x7E05, 0x2B56, 0x1867, 0x1B98, 0x28A9, 0x7DFA, 0x4ECB, 0xD75C, 0xE46D, 0xB13E, 0x820F, 0x9231, 0xA100, 0xF453, 0xC762, 0x5EF5, 0x6DC4, 0x3897, 0x0BA6, 0x18EB, 0x2BDA, 0x7E89, 0x4DB8, 0xD42F, 0xE71E, 0xB24D, 0x817C, 0x9142, 0xA273, 0xF720, 0xC411, 0x5D86, 0x6EB7, 0x3BE4, 0x08D5, 0x1D7E, 0x2E4F, 0x7B1C, 0x482D, 0xD1BA, 0xE28B, 0xB7D8, 0x84E9, 0x94D7, 0xA7E6, 0xF2B5, 0xC184, 0x5813, 0x6B22, 0x3E71, 0x0D40, 0x1E0D, 0x2D3C, 0x786F, 0x4B5E, 0xD2C9, 0xE1F8, 0xB4AB, 0x879A, 0x97A4, 0xA495, 0xF1C6, 0xC2F7, 0x5B60, 0x6851, 0x3D02, 0x0E33, 0x1654, 0x2565, 0x7036, 0x4307, 0xDA90, 0xE9A1, 0xBCF2, 0x8FC3, 0x9FFD, 0xACCC, 0xF99F, 0xCAAE, 0x5339, 0x6008, 0x355B, 0x066A, 0x1527, 0x2616, 0x7345, 0x4074, 0xD9E3, 0xEAD2, 0xBF81, 0x8CB0, 0x9C8E, 0xAFBF, 0xFAEC, 0xC9DD, 0x504A, 0x637B, 0x3628, 0x0519, 0x10B2, 0x2383, 0x76D0, 0x45E1, 0xDC76, 0xEF47, 0xBA14, 0x8925, 0x991B, 0xAA2A, 0xFF79, 0xCC48, 0x55DF, 0x66EE, 0x33BD, 0x008C, 0x13C1, 0x20F0, 0x75A3, 0x4692, 0xDF05, 0xEC34, 0xB967, 0x8A56, 0x9A68, 0xA959, 0xFC0A, 0xCF3B, 0x56AC, 0x659D, 0x30CE, 0x03FF, }, { 0x0000, 0x3730, 0x6E60, 0x5950, 0xDCC0, 0xEBF0, 0xB2A0, 0x8590, 0xA9A1, 0x9E91, 0xC7C1, 0xF0F1, 0x7561, 0x4251, 0x1B01, 0x2C31, 0x4363, 0x7453, 0x2D03, 0x1A33, 0x9FA3, 0xA893, 0xF1C3, 0xC6F3, 0xEAC2, 0xDDF2, 0x84A2, 0xB392, 0x3602, 0x0132, 0x5862, 0x6F52, 0x86C6, 0xB1F6, 0xE8A6, 0xDF96, 0x5A06, 0x6D36, 0x3466, 0x0356, 0x2F67, 0x1857, 0x4107, 0x7637, 0xF3A7, 0xC497, 0x9DC7, 0xAAF7, 0xC5A5, 0xF295, 0xABC5, 0x9CF5, 0x1965, 0x2E55, 0x7705, 0x4035, 0x6C04, 0x5B34, 0x0264, 0x3554, 0xB0C4, 0x87F4, 0xDEA4, 0xE994, 0x1DAD, 0x2A9D, 0x73CD, 0x44FD, 0xC16D, 0xF65D, 0xAF0D, 0x983D, 0xB40C, 0x833C, 0xDA6C, 0xED5C, 0x68CC, 0x5FFC, 0x06AC, 0x319C, 0x5ECE, 0x69FE, 0x30AE, 0x079E, 0x820E, 0xB53E, 0xEC6E, 0xDB5E, 0xF76F, 0xC05F, 0x990F, 0xAE3F, 0x2BAF, 0x1C9F, 0x45CF, 0x72FF, 0x9B6B, 0xAC5B, 0xF50B, 0xC23B, 0x47AB, 0x709B, 0x29CB, 0x1EFB, 0x32CA, 0x05FA, 0x5CAA, 0x6B9A, 0xEE0A, 0xD93A, 0x806A, 0xB75A, 0xD808, 0xEF38, 0xB668, 0x8158, 0x04C8, 0x33F8, 0x6AA8, 0x5D98, 0x71A9, 0x4699, 0x1FC9, 0x28F9, 0xAD69, 0x9A59, 0xC309, 0xF439, 0x3B5A, 0x0C6A, 0x553A, 0x620A, 0xE79A, 0xD0AA, 0x89FA, 0xBECA, 0x92FB, 0xA5CB, 0xFC9B, 0xCBAB, 0x4E3B, 0x790B, 0x205B, 0x176B, 0x7839, 0x4F09, 0x1659, 0x2169, 0xA4F9, 0x93C9, 0xCA99, 0xFDA9, 0xD198, 0xE6A8, 0xBFF8, 0x88C8, 0x0D58, 0x3A68, 0x6338, 0x5408, 0xBD9C, 0x8AAC, 0xD3FC, 0xE4CC, 0x615C, 0x566C, 0x0F3C, 0x380C, 0x143D, 0x230D, 0x7A5D, 0x4D6D, 0xC8FD, 0xFFCD, 0xA69D, 0x91AD, 0xFEFF, 0xC9CF, 0x909F, 0xA7AF, 0x223F, 0x150F, 0x4C5F, 0x7B6F, 0x575E, 0x606E, 0x393E, 0x0E0E, 0x8B9E, 0xBCAE, 0xE5FE, 0xD2CE, 0x26F7, 0x11C7, 0x4897, 0x7FA7, 0xFA37, 0xCD07, 0x9457, 0xA367, 0x8F56, 0xB866, 0xE136, 0xD606, 0x5396, 0x64A6, 0x3DF6, 0x0AC6, 0x6594, 0x52A4, 0x0BF4, 0x3CC4, 0xB954, 0x8E64, 0xD734, 0xE004, 0xCC35, 0xFB05, 0xA255, 0x9565, 0x10F5, 0x27C5, 0x7E95, 0x49A5, 0xA031, 0x9701, 0xCE51, 0xF961, 0x7CF1, 0x4BC1, 0x1291, 0x25A1, 0x0990, 0x3EA0, 0x67F0, 0x50C0, 0xD550, 0xE260, 0xBB30, 0x8C00, 0xE352, 0xD462, 0x8D32, 0xBA02, 0x3F92, 0x08A2, 0x51F2, 0x66C2, 0x4AF3, 0x7DC3, 0x2493, 0x13A3, 0x9633, 0xA103, 0xF853, 0xCF63, }, { 0x0000, 0x76B4, 0xED68, 0x9BDC, 0xCAF1, 0xBC45, 0x2799, 0x512D, 0x85C3, 0xF377, 0x68AB, 0x1E1F, 0x4F32, 0x3986, 0xA25A, 0xD4EE, 0x1BA7, 0x6D13, 0xF6CF, 0x807B, 0xD156, 0xA7E2, 0x3C3E, 0x4A8A, 0x9E64, 0xE8D0, 0x730C, 0x05B8, 0x5495, 0x2221, 0xB9FD, 0xCF49, 0x374E, 0x41FA, 0xDA26, 0xAC92, 0xFDBF, 0x8B0B, 0x10D7, 0x6663, 0xB28D, 0xC439, 0x5FE5, 0x2951, 0x787C, 0x0EC8, 0x9514, 0xE3A0, 0x2CE9, 0x5A5D, 0xC181, 0xB735, 0xE618, 0x90AC, 0x0B70, 0x7DC4, 0xA92A, 0xDF9E, 0x4442, 0x32F6, 0x63DB, 0x156F, 0x8EB3, 0xF807, 0x6E9C, 0x1828, 0x83F4, 0xF540, 0xA46D, 0xD2D9, 0x4905, 0x3FB1, 0xEB5F, 0x9DEB, 0x0637, 0x7083, 0x21AE, 0x571A, 0xCCC6, 0xBA72, 0x753B, 0x038F, 0x9853, 0xEEE7, 0xBFCA, 0xC97E, 0x52A2, 0x2416, 0xF0F8, 0x864C, 0x1D90, 0x6B24, 0x3A09, 0x4CBD, 0xD761, 0xA1D5, 0x59D2, 0x2F66, 0xB4BA, 0xC20E, 0x9323, 0xE597, 0x7E4B, 0x08FF, 0xDC11, 0xAAA5, 0x3179, 0x47CD, 0x16E0, 0x6054, 0xFB88, 0x8D3C, 0x4275, 0x34C1, 0xAF1D, 0xD9A9, 0x8884, 0xFE30, 0x65EC, 0x1358, 0xC7B6, 0xB102, 0x2ADE, 0x5C6A, 0x0D47, 0x7BF3, 0xE02F, 0x969B, 0xDD38, 0xAB8C, 0x3050, 0x46E4, 0x17C9, 0x617D, 0xFAA1, 0x8C15, 0x58FB, 0x2E4F, 0xB593, 0xC327, 0x920A, 0xE4BE, 0x7F62, 0x09D6, 0xC69F, 0xB02B, 0x2BF7, 0x5D43, 0x0C6E, 0x7ADA, 0xE106, 0x97B2, 0x435C, 0x35E8, 0xAE34, 0xD880, 0x89AD, 0xFF19, 0x64C5, 0x1271, 0xEA76, 0x9CC2, 0x071E, 0x71AA, 0x2087, 0x5633, 0xCDEF, 0xBB5B, 0x6FB5, 0x1901, 0x82DD, 0xF469, 0xA544, 0xD3F0, 0x482C, 0x3E98, 0xF1D1, 0x8765, 0x1CB9, 0x6A0D, 0x3B20, 0x4D94, 0xD648, 0xA0FC, 0x7412, 0x02A6, 0x997A, 0xEFCE, 0xBEE3, 0xC857, 0x538B, 0x253F, 0xB3A4, 0xC510, 0x5ECC, 0x2878, 0x7955, 0x0FE1, 0x943D, 0xE289, 0x3667, 0x40D3, 0xDB0F, 0xADBB, 0xFC96, 0x8A22, 0x11FE, 0x674A, 0xA803, 0xDEB7, 0x456B, 0x33DF, 0x62F2, 0x1446, 0x8F9A, 0xF92E, 0x2DC0, 0x5B74, 0xC0A8, 0xB61C, 0xE731, 0x9185, 0x0A59, 0x7CED, 0x84EA, 0xF25E, 0x6982, 0x1F36, 0x4E1B, 0x38AF, 0xA373, 0xD5C7, 0x0129, 0x779D, 0xEC41, 0x9AF5, 0xCBD8, 0xBD6C, 0x26B0, 0x5004, 0x9F4D, 0xE9F9, 0x7225, 0x0491, 0x55BC, 0x2308, 0xB8D4, 0xCE60, 0x1A8E, 0x6C3A, 0xF7E6, 0x8152, 0xD07F, 0xA6CB, 0x3D17, 0x4BA3, }, { 0x0000, 0xAA51, 0x4483, 0xEED2, 0x8906, 0x2357, 0xCD85, 0x67D4, 0x022D, 0xA87C, 0x46AE, 0xECFF, 0x8B2B, 0x217A, 0xCFA8, 0x65F9, 0x045A, 0xAE0B, 0x40D9, 0xEA88, 0x8D5C, 0x270D, 0xC9DF, 0x638E, 0x0677, 0xAC26, 0x42F4, 0xE8A5, 0x8F71, 0x2520, 0xCBF2, 0x61A3, 0x08B4, 0xA2E5, 0x4C37, 0xE666, 0x81B2, 0x2BE3, 0xC531, 0x6F60, 0x0A99, 0xA0C8, 0x4E1A, 0xE44B, 0x839F, 0x29CE, 0xC71C, 0x6D4D, 0x0CEE, 0xA6BF, 0x486D, 0xE23C, 0x85E8, 0x2FB9, 0xC16B, 0x6B3A, 0x0EC3, 0xA492, 0x4A40, 0xE011, 0x87C5, 0x2D94, 0xC346, 0x6917, 0x1168, 0xBB39, 0x55EB, 0xFFBA, 0x986E, 0x323F, 0xDCED, 0x76BC, 0x1345, 0xB914, 0x57C6, 0xFD97, 0x9A43, 0x3012, 0xDEC0, 0x7491, 0x1532, 0xBF63, 0x51B1, 0xFBE0, 0x9C34, 0x3665, 0xD8B7, 0x72E6, 0x171F, 0xBD4E, 0x539C, 0xF9CD, 0x9E19, 0x3448, 0xDA9A, 0x70CB, 0x19DC, 0xB38D, 0x5D5F, 0xF70E, 0x90DA, 0x3A8B, 0xD459, 0x7E08, 0x1BF1, 0xB1A0, 0x5F72, 0xF523, 0x92F7, 0x38A6, 0xD674, 0x7C25, 0x1D86, 0xB7D7, 0x5905, 0xF354, 0x9480, 0x3ED1, 0xD003, 0x7A52, 0x1FAB, 0xB5FA, 0x5B28, 0xF179, 0x96AD, 0x3CFC, 0xD22E, 0x787F, 0x22D0, 0x8881, 0x6653, 0xCC02, 0xABD6, 0x0187, 0xEF55, 0x4504, 0x20FD, 0x8AAC, 0x647E, 0xCE2F, 0xA9FB, 0x03AA, 0xED78, 0x4729, 0x268A, 0x8CDB, 0x6209, 0xC858, 0xAF8C, 0x05DD, 0xEB0F, 0x415E, 0x24A7, 0x8EF6, 0x6024, 0xCA75, 0xADA1, 0x07F0, 0xE922, 0x4373, 0x2A64, 0x8035, 0x6EE7, 0xC4B6, 0xA362, 0x0933, 0xE7E1, 0x4DB0, 0x2849, 0x8218, 0x6CCA, 0xC69B, 0xA14F, 0x0B1E, 0xE5CC, 0x4F9D, 0x2E3E, 0x846F, 0x6ABD, 0xC0EC, 0xA738, 0x0D69, 0xE3BB, 0x49EA, 0x2C13, 0x8642, 0x6890, 0xC2C1, 0xA515, 0x0F44, 0xE196, 0x4BC7, 0x33B8, 0x99E9, 0x773B, 0xDD6A, 0xBABE, 0x10EF, 0xFE3D, 0x546C, 0x3195, 0x9BC4, 0x7516, 0xDF47, 0xB893, 0x12C2, 0xFC10, 0x5641, 0x37E2, 0x9DB3, 0x7361, 0xD930, 0xBEE4, 0x14B5, 0xFA67, 0x5036, 0x35CF, 0x9F9E, 0x714C, 0xDB1D, 0xBCC9, 0x1698, 0xF84A, 0x521B, 0x3B0C, 0x915D, 0x7F8F, 0xD5DE, 0xB20A, 0x185B, 0xF689, 0x5CD8, 0x3921, 0x9370, 0x7DA2, 0xD7F3, 0xB027, 0x1A76, 0xF4A4, 0x5EF5, 0x3F56, 0x9507, 0x7BD5, 0xD184, 0xB650, 0x1C01, 0xF2D3, 0x5882, 0x3D7B, 0x972A, 0x79F8, 0xD3A9, 0xB47D, 0x1E2C, 0xF0FE, 0x5AAF, }, { 0x0000, 0x45A0, 0x8B40, 0xCEE0, 0x06A1, 0x4301, 0x8DE1, 0xC841, 0x0D42, 0x48E2, 0x8602, 0xC3A2, 0x0BE3, 0x4E43, 0x80A3, 0xC503, 0x1A84, 0x5F24, 0x91C4, 0xD464, 0x1C25, 0x5985, 0x9765, 0xD2C5, 0x17C6, 0x5266, 0x9C86, 0xD926, 0x1167, 0x54C7, 0x9A27, 0xDF87, 0x3508, 0x70A8, 0xBE48, 0xFBE8, 0x33A9, 0x7609, 0xB8E9, 0xFD49, 0x384A, 0x7DEA, 0xB30A, 0xF6AA, 0x3EEB, 0x7B4B, 0xB5AB, 0xF00B, 0x2F8C, 0x6A2C, 0xA4CC, 0xE16C, 0x292D, 0x6C8D, 0xA26D, 0xE7CD, 0x22CE, 0x676E, 0xA98E, 0xEC2E, 0x246F, 0x61CF, 0xAF2F, 0xEA8F, 0x6A10, 0x2FB0, 0xE150, 0xA4F0, 0x6CB1, 0x2911, 0xE7F1, 0xA251, 0x6752, 0x22F2, 0xEC12, 0xA9B2, 0x61F3, 0x2453, 0xEAB3, 0xAF13, 0x7094, 0x3534, 0xFBD4, 0xBE74, 0x7635, 0x3395, 0xFD75, 0xB8D5, 0x7DD6, 0x3876, 0xF696, 0xB336, 0x7B77, 0x3ED7, 0xF037, 0xB597, 0x5F18, 0x1AB8, 0xD458, 0x91F8, 0x59B9, 0x1C19, 0xD2F9, 0x9759, 0x525A, 0x17FA, 0xD91A, 0x9CBA, 0x54FB, 0x115B, 0xDFBB, 0x9A1B, 0x459C, 0x003C, 0xCEDC, 0x8B7C, 0x433D, 0x069D, 0xC87D, 0x8DDD, 0x48DE, 0x0D7E, 0xC39E, 0x863E, 0x4E7F, 0x0BDF, 0xC53F, 0x809F, 0xD420, 0x9180, 0x5F60, 0x1AC0, 0xD281, 0x9721, 0x59C1, 0x1C61, 0xD962, 0x9CC2, 0x5222, 0x1782, 0xDFC3, 0x9A63, 0x5483, 0x1123, 0xCEA4, 0x8B04, 0x45E4, 0x0044, 0xC805, 0x8DA5, 0x4345, 0x06E5, 0xC3E6, 0x8646, 0x48A6, 0x0D06, 0xC547, 0x80E7, 0x4E07, 0x0BA7, 0xE128, 0xA488, 0x6A68, 0x2FC8, 0xE789, 0xA229, 0x6CC9, 0x2969, 0xEC6A, 0xA9CA, 0x672A, 0x228A, 0xEACB, 0xAF6B, 0x618B, 0x242B, 0xFBAC, 0xBE0C, 0x70EC, 0x354C, 0xFD0D, 0xB8AD, 0x764D, 0x33ED, 0xF6EE, 0xB34E, 0x7DAE, 0x380E, 0xF04F, 0xB5EF, 0x7B0F, 0x3EAF, 0xBE30, 0xFB90, 0x3570, 0x70D0, 0xB891, 0xFD31, 0x33D1, 0x7671, 0xB372, 0xF6D2, 0x3832, 0x7D92, 0xB5D3, 0xF073, 0x3E93, 0x7B33, 0xA4B4, 0xE114, 0x2FF4, 0x6A54, 0xA215, 0xE7B5, 0x2955, 0x6CF5, 0xA9F6, 0xEC56, 0x22B6, 0x6716, 0xAF57, 0xEAF7, 0x2417, 0x61B7, 0x8B38, 0xCE98, 0x0078, 0x45D8, 0x8D99, 0xC839, 0x06D9, 0x4379, 0x867A, 0xC3DA, 0x0D3A, 0x489A, 0x80DB, 0xC57B, 0x0B9B, 0x4E3B, 0x91BC, 0xD41C, 0x1AFC, 0x5F5C, 0x971D, 0xD2BD, 0x1C5D, 0x59FD, 0x9CFE, 0xD95E, 0x17BE, 0x521E, 0x9A5F, 0xDFFF, 0x111F, 0x54BF, }, { 0x0000, 0xB861, 0x60E3, 0xD882, 0xC1C6, 0x79A7, 0xA125, 0x1944, 0x93AD, 0x2BCC, 0xF34E, 0x4B2F, 0x526B, 0xEA0A, 0x3288, 0x8AE9, 0x377B, 0x8F1A, 0x5798, 0xEFF9, 0xF6BD, 0x4EDC, 0x965E, 0x2E3F, 0xA4D6, 0x1CB7, 0xC435, 0x7C54, 0x6510, 0xDD71, 0x05F3, 0xBD92, 0x6EF6, 0xD697, 0x0E15, 0xB674, 0xAF30, 0x1751, 0xCFD3, 0x77B2, 0xFD5B, 0x453A, 0x9DB8, 0x25D9, 0x3C9D, 0x84FC, 0x5C7E, 0xE41F, 0x598D, 0xE1EC, 0x396E, 0x810F, 0x984B, 0x202A, 0xF8A8, 0x40C9, 0xCA20, 0x7241, 0xAAC3, 0x12A2, 0x0BE6, 0xB387, 0x6B05, 0xD364, 0xDDEC, 0x658D, 0xBD0F, 0x056E, 0x1C2A, 0xA44B, 0x7CC9, 0xC4A8, 0x4E41, 0xF620, 0x2EA2, 0x96C3, 0x8F87, 0x37E6, 0xEF64, 0x5705, 0xEA97, 0x52F6, 0x8A74, 0x3215, 0x2B51, 0x9330, 0x4BB2, 0xF3D3, 0x793A, 0xC15B, 0x19D9, 0xA1B8, 0xB8FC, 0x009D, 0xD81F, 0x607E, 0xB31A, 0x0B7B, 0xD3F9, 0x6B98, 0x72DC, 0xCABD, 0x123F, 0xAA5E, 0x20B7, 0x98D6, 0x4054, 0xF835, 0xE171, 0x5910, 0x8192, 0x39F3, 0x8461, 0x3C00, 0xE482, 0x5CE3, 0x45A7, 0xFDC6, 0x2544, 0x9D25, 0x17CC, 0xAFAD, 0x772F, 0xCF4E, 0xD60A, 0x6E6B, 0xB6E9, 0x0E88, 0xABF9, 0x1398, 0xCB1A, 0x737B, 0x6A3F, 0xD25E, 0x0ADC, 0xB2BD, 0x3854, 0x8035, 0x58B7, 0xE0D6, 0xF992, 0x41F3, 0x9971, 0x2110, 0x9C82, 0x24E3, 0xFC61, 0x4400, 0x5D44, 0xE525, 0x3DA7, 0x85C6, 0x0F2F, 0xB74E, 0x6FCC, 0xD7AD, 0xCEE9, 0x7688, 0xAE0A, 0x166B, 0xC50F, 0x7D6E, 0xA5EC, 0x1D8D, 0x04C9, 0xBCA8, 0x642A, 0xDC4B, 0x56A2, 0xEEC3, 0x3641, 0x8E20, 0x9764, 0x2F05, 0xF787, 0x4FE6, 0xF274, 0x4A15, 0x9297, 0x2AF6, 0x33B2, 0x8BD3, 0x5351, 0xEB30, 0x61D9, 0xD9B8, 0x013A, 0xB95B, 0xA01F, 0x187E, 0xC0FC, 0x789D, 0x7615, 0xCE74, 0x16F6, 0xAE97, 0xB7D3, 0x0FB2, 0xD730, 0x6F51, 0xE5B8, 0x5DD9, 0x855B, 0x3D3A, 0x247E, 0x9C1F, 0x449D, 0xFCFC, 0x416E, 0xF90F, 0x218D, 0x99EC, 0x80A8, 0x38C9, 0xE04B, 0x582A, 0xD2C3, 0x6AA2, 0xB220, 0x0A41, 0x1305, 0xAB64, 0x73E6, 0xCB87, 0x18E3, 0xA082, 0x7800, 0xC061, 0xD925, 0x6144, 0xB9C6, 0x01A7, 0x8B4E, 0x332F, 0xEBAD, 0x53CC, 0x4A88, 0xF2E9, 0x2A6B, 0x920A, 0x2F98, 0x97F9, 0x4F7B, 0xF71A, 0xEE5E, 0x563F, 0x8EBD, 0x36DC, 0xBC35, 0x0454, 0xDCD6, 0x64B7, 0x7DF3, 0xC592, 0x1D10, 0xA571, }, { 0x0000, 0x47D3, 0x8FA6, 0xC875, 0x0F6D, 0x48BE, 0x80CB, 0xC718, 0x1EDA, 0x5909, 0x917C, 0xD6AF, 0x11B7, 0x5664, 0x9E11, 0xD9C2, 0x3DB4, 0x7A67, 0xB212, 0xF5C1, 0x32D9, 0x750A, 0xBD7F, 0xFAAC, 0x236E, 0x64BD, 0xACC8, 0xEB1B, 0x2C03, 0x6BD0, 0xA3A5, 0xE476, 0x7B68, 0x3CBB, 0xF4CE, 0xB31D, 0x7405, 0x33D6, 0xFBA3, 0xBC70, 0x65B2, 0x2261, 0xEA14, 0xADC7, 0x6ADF, 0x2D0C, 0xE579, 0xA2AA, 0x46DC, 0x010F, 0xC97A, 0x8EA9, 0x49B1, 0x0E62, 0xC617, 0x81C4, 0x5806, 0x1FD5, 0xD7A0, 0x9073, 0x576B, 0x10B8, 0xD8CD, 0x9F1E, 0xF6D0, 0xB103, 0x7976, 0x3EA5, 0xF9BD, 0xBE6E, 0x761B, 0x31C8, 0xE80A, 0xAFD9, 0x67AC, 0x207F, 0xE767, 0xA0B4, 0x68C1, 0x2F12, 0xCB64, 0x8CB7, 0x44C2, 0x0311, 0xC409, 0x83DA, 0x4BAF, 0x0C7C, 0xD5BE, 0x926D, 0x5A18, 0x1DCB, 0xDAD3, 0x9D00, 0x5575, 0x12A6, 0x8DB8, 0xCA6B, 0x021E, 0x45CD, 0x82D5, 0xC506, 0x0D73, 0x4AA0, 0x9362, 0xD4B1, 0x1CC4, 0x5B17, 0x9C0F, 0xDBDC, 0x13A9, 0x547A, 0xB00C, 0xF7DF, 0x3FAA, 0x7879, 0xBF61, 0xF8B2, 0x30C7, 0x7714, 0xAED6, 0xE905, 0x2170, 0x66A3, 0xA1BB, 0xE668, 0x2E1D, 0x69CE, 0xFD81, 0xBA52, 0x7227, 0x35F4, 0xF2EC, 0xB53F, 0x7D4A, 0x3A99, 0xE35B, 0xA488, 0x6CFD, 0x2B2E, 0xEC36, 0xABE5, 0x6390, 0x2443, 0xC035, 0x87E6, 0x4F93, 0x0840, 0xCF58, 0x888B, 0x40FE, 0x072D, 0xDEEF, 0x993C, 0x5149, 0x169A, 0xD182, 0x9651, 0x5E24, 0x19F7, 0x86E9, 0xC13A, 0x094F, 0x4E9C, 0x8984, 0xCE57, 0x0622, 0x41F1, 0x9833, 0xDFE0, 0x1795, 0x5046, 0x975E, 0xD08D, 0x18F8, 0x5F2B, 0xBB5D, 0xFC8E, 0x34FB, 0x7328, 0xB430, 0xF3E3, 0x3B96, 0x7C45, 0xA587, 0xE254, 0x2A21, 0x6DF2, 0xAAEA, 0xED39, 0x254C, 0x629F, 0x0B51, 0x4C82, 0x84F7, 0xC324, 0x043C, 0x43EF, 0x8B9A, 0xCC49, 0x158B, 0x5258, 0x9A2D, 0xDDFE, 0x1AE6, 0x5D35, 0x9540, 0xD293, 0x36E5, 0x7136, 0xB943, 0xFE90, 0x3988, 0x7E5B, 0xB62E, 0xF1FD, 0x283F, 0x6FEC, 0xA799, 0xE04A, 0x2752, 0x6081, 0xA8F4, 0xEF27, 0x7039, 0x37EA, 0xFF9F, 0xB84C, 0x7F54, 0x3887, 0xF0F2, 0xB721, 0x6EE3, 0x2930, 0xE145, 0xA696, 0x618E, 0x265D, 0xEE28, 0xA9FB, 0x4D8D, 0x0A5E, 0xC22B, 0x85F8, 0x42E0, 0x0533, 0xCD46, 0x8A95, 0x5357, 0x1484, 0xDCF1, 0x9B22, 0x5C3A, 0x1BE9, 0xD39C, 0x944F, }, }; } // namespace openmsx #if 0 #include int main() { openmsx::CRC16 crc; assert(crc.getValue() == 0xFFFF); crc.update(0xA1); crc.update(0xA1); crc.update(0xA1); assert(crc.getValue() == 0xCDB4); } #endif #if 0 // Generator for the table above #include using namespace openmsx; int main() { uint16_t tab[8][256]; for (unsigned i = 0; i < 0x100; ++i) { uint16_t x = i << 8; for (int j = 0; j < 8; ++j) { x = (x << 1) ^ ((x & 0x8000) ? 0x1021 : 0); } tab[0][i] = x; } for (unsigned i = 0; i < 0x100; ++i) { uint16_t c = tab[0][i]; for (unsigned j = 1; j < 8; ++j) { c = tab[0][c >> 8] ^ (c << 8); tab[j][i] = c; } } printf("const uint16_t CRC16::tab[8][256] = {\n"); for (int i = 0; i < 8; ++i) { printf("\t{\n"); for (int j = 0; j < 0x100; ++j) { printf((j & 7) ? " " : "\t\t"); printf("0x%04X,", tab[i][j]); if ((j & 7) == 7) printf("\n"); } printf("\t},\n"); } printf("};\n"); } #endif openmsx-0.10.0/src/utils/HexDump.cc0000644000175000017500000000232712262345041017660 0ustar manuelmanuel00000000000000#include "HexDump.hh" #include namespace HexDump { using std::string; typedef unsigned char byte; static char encode2(byte x) { return (x < 10) ? (x + '0') : (x - 10 + 'A'); } static string encode(byte x) { string result; result += encode2(x >> 4); result += encode2(x & 15); return result; } string encode(const void* input_, size_t len, bool newlines) { auto input = static_cast(input_); string ret; while (len) { if (newlines && !ret.empty()) ret += '\n'; int t = int(std::min(16, len)); for (int i = 0; i < t; ++i) { ret += encode(*input++); if (i != 15) ret += ' '; } len -= t; } return ret; } static int decode(char x) { if (('0' <= x) && (x <= '9')) { return x - '0'; } else if (('A' <= x) && (x <= 'F')) { return x - 'A' + 10; } else if (('a' <= x) && (x <= 'f')) { return x - 'a' + 10; } else { return -1; } } string decode(const string& input) { string ret; const size_t len = input.size(); bool flip = true; char tmp = 0; for (size_t in = 0; in < len; ++in) { int d = decode(input[in]); if (d == -1) continue; if (flip) { tmp = d << 4; } else { tmp |= d; ret += tmp; } flip = !flip; } return ret; } } // namespace HexDump openmsx-0.10.0/src/utils/KeyRange.hh0000644000175000017500000000274612262345041020032 0ustar manuelmanuel00000000000000#ifndef KEYRANGE_HH #define KEYRANGE_HH #include "StringMap.hh" template class KeyIterator { public: KeyIterator(typename MAP::const_iterator it_) : it(it_) {} const typename MAP::key_type& operator*() const { return it->first; } KeyIterator& operator++() { ++it; return *this; } bool operator==(KeyIterator& other) const { return it == other.it; } bool operator!=(KeyIterator& other) const { return it != other.it; } private: typename MAP::const_iterator it; }; template class KeyIterator2 { public: KeyIterator2(typename StringMap::const_iterator it_) : it(it_) {} string_ref operator*() const { return it->first(); } KeyIterator2& operator++() { ++it; return *this; } bool operator==(KeyIterator2& other) const { return it == other.it; } bool operator!=(KeyIterator2& other) const { return it != other.it; } private: typename StringMap::const_iterator it; }; template class KeyRange { public: KeyRange(const MAP& map_) : map(map_) {} KeyIterator begin() const { return map.begin(); } KeyIterator end() const { return map.end(); } private: const MAP& map; }; template class KeyRange> { public: KeyRange(const StringMap& map_) : map(map_) {} KeyIterator2 begin() const { return map.begin(); } KeyIterator2 end() const { return map.end(); } private: const StringMap& map; }; template KeyRange keys(const MAP& map) { return KeyRange(map); } #endif openmsx-0.10.0/src/utils/checked_cast.hh0000644000175000017500000000326312262345041020720 0ustar manuelmanuel00000000000000#ifndef CHECKED_CAST_HH #define CHECKED_CAST_HH /** * Based on checked_cast implementation from the book: * C++ Coding Standard * item 93: Avoid using static_cast on pointers */ #include /* IMHO this implementation is simpler, but gcc-3.4 and below don't * correctly handle overloading with it (ambiguous overload). * * template struct remove_reference * { * typedef T type; * }; * template struct remove_reference * { * typedef T type; * }; * * template * static TO checked_cast(FROM* from) * { * assert(dynamic_cast(from) == static_cast(from)); * return static_cast(from); * } * template * static TO checked_cast(FROM& from) * { * typedef typename remove_reference::type* TO_PTR; * assert(dynamic_cast(&from) == static_cast(&from)); * return static_cast(from); * } * * Implementation below can only handle const references, need to find a way * around that. */ template struct checked_cast_impl {}; template struct checked_cast_impl { inline TO* operator()(FROM from) { assert(dynamic_cast(from) == static_cast(from)); return static_cast(from); } }; template struct checked_cast_impl { inline TO& operator()(const FROM& from) { assert(dynamic_cast(&from) == static_cast(&from)); return static_cast(from); } }; template static inline TO checked_cast(const FROM& from) { checked_cast_impl caster; return caster(from); } #endif openmsx-0.10.0/src/utils/sha1.hh0000644000175000017500000000455112262345041017155 0ustar manuelmanuel00000000000000#ifndef SHA1_HH #define SHA1_HH #include "string_ref.hh" #include #include namespace openmsx { /** This class represents the result of a sha1 calculation (a 160-bit value). * Objects of this class can be constructed from/converted to 40-digit hex * string. * We treat the value '000...00' (all zeros) special. This value can be used * to indicate a null-sha1sum value (e.g. sha1 not yet calculated, or not * meaningful). In theory it's possible this special value is the result of an * actual sha1 calculation, but this has an _extremely_ low probability. */ class Sha1Sum { public: // note: default copy and assign are ok Sha1Sum(); /** Construct from string, throws when string is malformed. */ explicit Sha1Sum(string_ref hex); void parse40(const char* str); std::string toString() const; // Test or set 'null' value. bool empty() const; void clear(); bool operator==(const Sha1Sum& other) const; bool operator!=(const Sha1Sum& other) const; bool operator< (const Sha1Sum& other) const; private: uint32_t a[5]; friend class SHA1; }; class CliComm; class EventDistributor; /** Helper class to perform a sha1 calculation. * Basic usage: * - construct a SHA1 object * - repeatedly call update() * - call digest() to get the result * Alternatively, use calc() if all data can be passed at once (IOW when there * would be exactly one call to update() in the recipe above). */ class SHA1 { public: SHA1(); /** Incrementally calculate the hash value. */ void update(const uint8_t* data, size_t len); /** Get the final hash. After this method is called, calls to update() * are invalid. */ Sha1Sum digest(); /** Easier to use interface, if you can pass all data in one go. */ static Sha1Sum calc(const uint8_t* data, size_t len); /** Easier to use interface, if you can pass all data in one go. But * also report progress. * Note that this only works when the given file is calculated * completely, in one call. The caller is responsible to make sure * this is the case. */ static Sha1Sum calcWithProgress(const uint8_t* data, size_t len, const std::string& filename, CliComm& cliComm, EventDistributor& distributor); private: void transform(const uint8_t buffer[64]); void finalize(); uint64_t m_count; Sha1Sum m_state; uint8_t m_buffer[64]; bool m_finalized; }; } // namespace openmsx #endif openmsx-0.10.0/src/utils/uint128.hh0000644000175000017500000001065412262345041017534 0ustar manuelmanuel00000000000000#ifndef UINT128_HH #define UINT128_HH #include #if defined __x86_64 && !defined _MSC_VER // On 64-bit CPUs gcc already provides a 128-bit type, // use that type because it's most likely much more efficient. // VC++ 2008 does not provide a 128-bit integer type typedef __uint128_t uint128; inline uint64_t toUint64(uint128 a) { return a; } #else // __x86_64 && !_MSC_VER /** Unsigned 128-bit integer type. * Very simple implementation, not optimized for speed. * * Loosely based on the 128-bit utility classes written by * Jan Ringos, http://Tringi.Mx-3.cz */ class uint128 { public: uint128(const uint128& a) : lo(a.lo), hi(a.hi) {} uint128(uint64_t a) : lo(a), hi(0) {} bool operator!() const { return !(hi || lo); } uint128 operator~() const { return uint128(~lo, ~hi); } uint128 operator-() const { uint128 result = ~*this; ++result; return result; } uint128& operator++() { ++lo; if (lo == 0) ++hi; return *this; } uint128& operator--() { if (lo == 0) --hi; --lo; return *this; } uint128 operator++(int) { uint128 b = *this; ++*this; return b; } uint128 operator--(int) { uint128 b = *this; --*this; return b; } uint128& operator+=(const uint128& b) { uint64_t old_lo = lo; lo += b.lo; hi += b.hi + (lo < old_lo); return *this; } uint128& operator-=(const uint128& b) { return *this += (-b); } uint128& operator>>=(unsigned n) { n &= 0x7F; if (n < 64) { lo = (hi << (64 - n)) | (lo >> n); hi >>= n; } else { lo = hi >> (n - 64); hi = 0; } return *this; } uint128& operator<<=(unsigned n) { n &= 0x7F; if (n < 64) { hi = (hi << n) | (lo >> (64 - n)); lo <<= n; } else { hi = lo << (n - 64); lo = 0; } return *this; } uint128& operator|=(const uint128& b) { hi |= b.hi; lo |= b.lo; return *this; } uint128& operator&=(const uint128& b) { hi &= b.hi; lo &= b.lo; return *this; } uint128& operator^=(const uint128& b) { hi ^= b.hi; lo ^= b.lo; return *this; } uint128& operator/=(const uint128& b) { uint128 dummy; *this = div(b, dummy); return *this; } uint128& operator%=(const uint128& b) { div(b, *this); return *this; } uint128& operator*=(const uint128& b); private: uint128() {} uint128(uint64_t a, uint64_t b) : lo(a), hi(b) {} uint128 div(const uint128& ds, uint128& remainder) const; bool bit(unsigned n) const; void setBit(unsigned n); uint64_t lo; uint64_t hi; friend bool operator< (const uint128&, const uint128&); friend bool operator==(const uint128&, const uint128&); friend bool operator||(const uint128&, const uint128&); friend bool operator&&(const uint128&, const uint128&); friend uint64_t toUint64(const uint128&); }; inline uint128 operator+(const uint128& a, const uint128& b) { return uint128(a) += b; } inline uint128 operator-(const uint128& a, const uint128& b) { return uint128(a) -= b; } inline uint128 operator*(const uint128& a, const uint128& b) { return uint128(a) *= b; } inline uint128 operator/(const uint128& a, const uint128& b) { return uint128(a) /= b; } inline uint128 operator%(const uint128& a, const uint128& b) { return uint128(a) %= b; } inline uint128 operator>>(const uint128& a, unsigned n) { return uint128(a) >>= n; } inline uint128 operator<<(const uint128 & a, unsigned n) { return uint128(a) <<= n; } inline uint128 operator&(const uint128& a, const uint128& b) { return uint128(a) &= b; } inline uint128 operator|(const uint128& a, const uint128& b) { return uint128(a) |= b; } inline uint128 operator^(const uint128& a, const uint128& b) { return uint128(a) ^= b; } inline bool operator<(const uint128& a, const uint128& b) { return (a.hi == b.hi) ? (a.lo < b.lo) : (a.hi < b.hi); } inline bool operator>(const uint128& a, const uint128& b) { return b < a; } inline bool operator<=(const uint128& a, const uint128& b) { return !(b < a); } inline bool operator>=(const uint128& a, const uint128& b) { return !(a < b); } inline bool operator==(const uint128& a, const uint128& b) { return (a.hi == b.hi) && (a.lo == b.lo); } inline bool operator!=(const uint128& a, const uint128& b) { return !(a == b); } inline bool operator&&(const uint128& a, const uint128& b) { return (a.hi || a.lo) && (b.hi || b.lo); } inline bool operator||(const uint128& a, const uint128& b) { return a.hi || a.lo || b.hi || b.lo; } inline uint64_t toUint64(const uint128& a) { return a.lo; } #endif // __x86_64 && !_MSC_VER #endif // UINT128_HH openmsx-0.10.0/src/utils/Date.cc0000644000175000017500000000776112262345041017172 0ustar manuelmanuel00000000000000#include "Date.hh" #include #include #include #include namespace openmsx { namespace Date { const char* const days[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; const char* const months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; template static inline bool parseDigit(unsigned c, T& t) { c -= '0'; if (c > 9) return false; if (FIRST) { t = c * MUL; } else { t += c * MUL; } return true; } time_t fromString(const char* p) { struct tm tm; // skip day p += 3; // space if (*p++ != ' ') return time_t(-1); // Parse month switch (*p++) { case 'J': // Jan Jun Jul switch (*p++) { case 'a': // Jan if (*p++ != 'n') return time_t(-1); tm.tm_mon = 0; break; case 'u': // Jun Jul switch (*p++) { case 'n': tm.tm_mon = 5; break; case 'l': tm.tm_mon = 6; break; default: return time_t(-1); } break; default: return time_t(-1); } break; case 'F': // Feb if (*p++ != 'e') return time_t(-1); if (*p++ != 'b') return time_t(-1); tm.tm_mon = 1; break; case 'M': // Mar May if (*p++ != 'a') return time_t(-1); switch (*p++) { case 'r': tm.tm_mon = 2; break; case 'y': tm.tm_mon = 4; break; default: return time_t(-1); } break; case 'A': // Apr Aug switch (*p++) { case 'p': // Apr if (*p++ != 'r') return time_t(-1); tm.tm_mon = 3; break; case 'u': // Aug if (*p++ != 'g') return time_t(-1); tm.tm_mon = 7; break; default: return time_t(-1); } break; case 'S': // Sep if (*p++ != 'e') return time_t(-1); if (*p++ != 'p') return time_t(-1); tm.tm_mon = 8; break; case 'O': // Oct if (*p++ != 'c') return time_t(-1); if (*p++ != 't') return time_t(-1); tm.tm_mon = 9; break; case 'N': // Nov if (*p++ != 'o') return time_t(-1); if (*p++ != 'v') return time_t(-1); tm.tm_mon = 10; break; case 'D': // Dec if (*p++ != 'e') return time_t(-1); if (*p++ != 'c') return time_t(-1); tm.tm_mon = 11; break; default: return time_t(-1); } // space if (*p++ != ' ') return time_t(-1); // parse mday if (!parseDigit(*p++, tm.tm_mday)) return time_t(-1); if (!parseDigit(*p++, tm.tm_mday)) return time_t(-1); if ((tm.tm_mday < 1) || (31 < tm.tm_mday) ) return time_t(-1); // space if (*p++ != ' ') return time_t(-1); // parse hour if (!parseDigit(*p++, tm.tm_hour)) return time_t(-1); if (!parseDigit(*p++, tm.tm_hour)) return time_t(-1); if ((tm.tm_hour < 0) || (23 < tm.tm_hour)) return time_t(-1); // colon if (*p++ != ':') return time_t(-1); // parse minute if (!parseDigit(*p++, tm.tm_min)) return time_t(-1); if (!parseDigit(*p++, tm.tm_min)) return time_t(-1); if ((tm.tm_min < 0) || (59 < tm.tm_min)) return time_t(-1); // colon if (*p++ != ':') return time_t(-1); // parse second if (!parseDigit(*p++, tm.tm_sec)) return time_t(-1); if (!parseDigit(*p++, tm.tm_sec)) return time_t(-1); if ((tm.tm_sec < 0) || (59 < tm.tm_sec)) return time_t(-1); // space if (*p++ != ' ') return time_t(-1); // parse year if (!parseDigit(*p++, tm.tm_year)) return time_t(-1); if (!parseDigit(*p++, tm.tm_year)) return time_t(-1); if (!parseDigit(*p++, tm.tm_year)) return time_t(-1); if (!parseDigit(*p++, tm.tm_year)) return time_t(-1); tm.tm_year -= 1900; if (tm.tm_year < 0) return time_t(-1); tm.tm_isdst = -1; return mktime(&tm); } std::string toString(time_t time) { if (time < 0) time = 0; struct tm* tm; tm = localtime(&time); std::ostringstream sstr; sstr << std::setfill('0') << days [tm->tm_wday] << ' ' << months[tm->tm_mon] << ' ' << std::setw(2) << tm->tm_mday << ' ' << std::setw(2) << tm->tm_hour << ':' << std::setw(2) << tm->tm_min << ':' << std::setw(2) << tm->tm_sec << ' ' << std::setw(4) << (tm->tm_year + 1900); return sstr.str(); } } // namespace Date } // namespace openmsx openmsx-0.10.0/src/utils/AltSpaceSuppressor.hh0000644000175000017500000000151012262345041022113 0ustar manuelmanuel00000000000000#ifndef ALTSPACESUPPRESSOR_HH #define ALTSPACESUPPRESSOR_HH #ifdef _WIN32 #include namespace openmsx { // Utility class that stacks a WindowLongPtr value class WindowLongPtrStacker { public: WindowLongPtrStacker(int index, LONG_PTR value); void Push(HWND hWndArg); void Pop(); LONG_PTR GetOldValue(); private: HWND hWnd; int nIndex; LONG_PTR oldValue, newValue; }; // Suppressor of ALT+SPACE windows messages class AltSpaceSuppressor { public: static void Start(HWND hWnd); static void Stop(); private: static WindowLongPtrStacker procStacker; static LRESULT CALLBACK InterceptorWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); static bool SuppressAltSpace( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* outResult); }; } // namespace openmsx #endif #endif openmsx-0.10.0/src/utils/StringOp.hh0000644000175000017500000001137512262345041020070 0ustar manuelmanuel00000000000000#ifndef STRINGOP_HH #define STRINGOP_HH #include "string_ref.hh" #include "stringsp.hh" #include #include #include #include #include #include #include #if defined(__APPLE__) #include #endif namespace StringOp { class Builder { public: Builder(); ~Builder(); // Overloaded for the most common types, to avoid having to use // the templatized version below (expect for being implicitly // inlined, the template is just fine). Builder& operator<<(const std::string& t); Builder& operator<<(string_ref t); Builder& operator<<(const char* t); Builder& operator<<(unsigned char t); Builder& operator<<(unsigned short t); Builder& operator<<(unsigned t); Builder& operator<<(unsigned long t); Builder& operator<<(unsigned long long t); Builder& operator<<(char t); Builder& operator<<(short t); Builder& operator<<(int t); Builder& operator<<(long t); Builder& operator<<(long long t); Builder& operator<<(float t); Builder& operator<<(double t); // Templatized version is commented out. There's no problem in // enabling it, but the code works ATM without, and having it // disabled helps to catch future missing overloads in the // list above. /*template Builder& operator<<(const T& t) { buf += toString(t); return *this; }*/ //operator std::string() const; operator std::string() const { return buf; } operator string_ref() const { return buf; } private: std::string buf; }; // Generic toString implementation, works for all 'streamable' types. template std::string toString(const T& t) { std::ostringstream s; s << t; return s.str(); } // Overloads for specific types. These are much faster than the generic // version. Having them non-inline also reduces size of executable. std::string toString(long long a); std::string toString(unsigned long long a); std::string toString(long a); std::string toString(unsigned long a); std::string toString(int a); std::string toString(unsigned a); std::string toString(short a); std::string toString(unsigned short a); std::string toString(char a); std::string toString(signed char a); std::string toString(unsigned char a); std::string toString(bool a); // These are simple enough to inline. inline std::string toString(const char* s) { return s; } inline const std::string& toString(const std::string& s) { return s; } // Print the lower 'width' number of digits of 'a' in hex format // (without leading '0x' and with a-f in lower case). std::string toHexString(unsigned a, unsigned width); int stringToInt(const std::string& str); bool stringToInt(const std::string& str, int& result); unsigned stringToUint(const std::string& str); bool stringToUint(const std::string& str, unsigned& result); uint64_t stringToUint64(const std::string& str); bool stringToBool(string_ref str); double stringToDouble(const std::string& str); bool stringToDouble(const std::string& str, double& result); std::string toLower(string_ref str); bool startsWith(string_ref total, string_ref part); bool startsWith(string_ref total, char part); bool endsWith (string_ref total, string_ref part); bool endsWith (string_ref total, char part); void trimRight(std::string& str, const char* chars); void trimRight(std::string& str, char chars); void trimRight(string_ref& str, string_ref chars); void trimRight(string_ref& str, char chars); void trimLeft (std::string& str, const char* chars); void trimLeft (string_ref& str, string_ref chars); void splitOnFirst(string_ref str, string_ref chars, string_ref& first, string_ref& last); void splitOnFirst(string_ref str, char chars, string_ref& first, string_ref& last); void splitOnLast (string_ref str, string_ref chars, string_ref& first, string_ref& last); void splitOnLast (string_ref str, char chars, string_ref& first, string_ref& last); std::vector split(string_ref str, char chars); std::string join(const std::vector& elems, char separator); std::set parseRange(string_ref str, unsigned min, unsigned max); // case insensitive less-than operator struct caseless { bool operator()(string_ref s1, string_ref s2) const { auto m = std::min(s1.size(), s2.size()); int r = strncasecmp(s1.data(), s2.data(), m); return (r != 0) ? (r < 0) : (s1.size() < s2.size()); } }; struct casecmp { bool operator()(string_ref s1, string_ref s2) const { if (s1.size() != s2.size()) return false; return strncasecmp(s1.data(), s2.data(), s1.size()) == 0; } }; #if defined(__APPLE__) std::string fromCFString(CFStringRef str); #endif } #endif openmsx-0.10.0/src/utils/Math.cc0000644000175000017500000001367612262345041017210 0ustar manuelmanuel00000000000000#include "Math.hh" #include #include #if defined _MSC_VER && defined __x86_64 #include #endif namespace openmsx { #ifdef _MSC_VER // Poor man's implementations to get openmsx building with VC++ #ifdef __x86_64 // What follows below are C++ implementations of several C99 math functions missing // from VC++'s CRT as of 2008. These implementations using SSE/SSE2 intrinsics // for floating point operations. The (generally safe) assumption is that those // instructions are always available on CPUs capable of running 64-bit Windows. // // The assembler in Visual Studio (ml64.exe) no longer allows inline assembly, // so for ease of reading we've opted to use these intrinsic-based functions instead // of using the hand-written versions in a separate .asm file. The resulting // optimized code is pretty close to the hand-written code. // gcc-generated data section //;.LC0: //; .long 0 //; .long 1127219200 //; .section .rodata.cst16,"aM",@progbits,16 //; .align 16 //;.LC1: //; .long 4294967295 //; .long 2147483647 //; .long 0 //; .long 0 //; .section .rodata.cst8 //; .align 8 //;.LC2: //; .long 4294967295 //; .long 1071644671 //; .section .rodata.cst4,"aM",@progbits,4 //; .align 4 //;.LC3: //; .long 1258291200 //; .section .rodata.cst16 //; .align 16 //;.LC4: //; .long 2147483647 //; .long 0 //; .long 0 //; .long 0 //; .ident "GCC: (GNU) 4.4.0 20090130 (experimental)" //; .section .note.GNU-stack,"",@progbits // ML64 hand-written data section: //LC0 QWORD 4330000000000000h //LC1 QWORD 7FFFFFFFFFFFFFFFh //LC2 QWORD 3FDFFFFFFFFFFFFFh //LC3 DWORD 4B000000h //LC4 DWORD 7FFFFFFFh // lrint(): round double according to current floating-point rounding direction // gcc-generated: //; cvtsd2siq %xmm0, %rax //; ret // ML64 hand-written: //LEAF_ENTRY lrinta, Math // cvtsd2si rax, xmm0 // ret //LEAF_END lrinta, Math long lrint(double x) { return _mm_cvtsd_si32(_mm_load_sd(&x)); } // lrintf(): round float according to current floating-point rounding direction // gcc-generated: //; cvtss2siq %xmm0, %rax //; ret // ML64 hand-written: //LEAF_ENTRY lrintf, Math // cvtss2si rax, xmm0 // ret //LEAF_END lrintf, Math long lrintf(float x) { return _mm_cvtss_si32(_mm_load_ss(&x)); } // truncf(): round float to the nearest integer not larger in absolute value // gcc-generated: //; movss .LC4(%rip), %xmm1 //; movaps %xmm0, %xmm2 //; andps %xmm1, %xmm2 //; comiss .LC3(%rip), %xmm2 //; jae .L6 //; cvttss2si %xmm0, %eax //; cvtsi2ss %eax, %xmm0 //;.L6: //; rep //; ret // ML64 hand-written: //LEAF_ENTRY truncf, Math // movss xmm1, LC4 // movaps xmm2, xmm0 // andps xmm2, xmm1 // comiss xmm2, LC3 // jae L6 // cvttss2si eax, xmm0 // cvtsi2ss xmm0, eax //L6: // ret //LEAF_END truncf, Math float truncf(float x) { int64_t tempi = _mm_cvttss_si64(_mm_load_ss(&x)); __m128 xmmx = _mm_cvtsi64_ss(_mm_setzero_ps(), tempi); float ret; _mm_store_ss(&ret, xmmx); return ret; } // round(): round double to nearest integer, away from zero // gcc-generated: //; movsd .LC1(%rip), %xmm1 //; movapd %xmm0, %xmm2 //; andpd %xmm1, %xmm2 //; comisd .LC0(%rip), %xmm2 //; jae .L2 //; addsd .LC2(%rip), %xmm2 //; andnpd %xmm0, %xmm1 //; cvttsd2siq %xmm2, %rax //; cvtsi2sdq %rax, %xmm2 //; movapd %xmm2, %xmm0 //; orpd %xmm1, %xmm0 //;.L2: //; rep //; ret // ML64 hand-written: //LEAF_ENTRY round, Math // movsd xmm1, LC1 // movapd xmm2, xmm0 // andpd xmm2, xmm1 // comisd xmm2, LC0 // jae L2 // addsd xmm2, LC2 // andnpd xmm1, xmm0 // cvttsd2si rax, xmm2 // cvtsi2sd xmm2, rax // movapd xmm0, xmm2 // orpd xmm0, xmm1 //L2: // ret //LEAF_END round, Math double round(double x) { const double half = 0.5; __m128d xmmhalf = _mm_load_sd(&half); __m128d xmmx = _mm_load_sd(&x); // Add + or -0.5 depending on sign... if (_mm_comige_sd(xmmx, _mm_setzero_pd())) { xmmx = _mm_add_sd(xmmx, xmmhalf); } else { xmmx = _mm_sub_sd(xmmx, xmmhalf); } // ... then round towards zero int64_t tempi64 = _mm_cvttsd_si64(xmmx); xmmx = _mm_cvtsi64_sd(_mm_setzero_pd(), tempi64); double ret; _mm_store_sd(&ret, xmmx); return ret; } #else // lrint(): round double according to current floating-point rounding direction long lrint(double x) { long retval; __asm { fld qword ptr [x] fistp dword ptr [retval] } return retval; } // lrint(): round float according to current floating-point rounding direction long lrintf(float x) { long retval; __asm { fld dword ptr [x] fistp dword ptr [retval] } return retval; } // trunc(): round double to the nearest integer not larger in absolute value double trunc(double x) { short ctrl1, ctrl2; double retval; __asm { fnstcw word ptr [ctrl1] movzx eax,word ptr [ctrl1] or ah,0Ch mov word ptr [ctrl2],ax fldcw word ptr [ctrl2] fld qword ptr [x] frndint fldcw word ptr [ctrl1] fstp qword ptr [retval] } return retval; } // truncf(): round float to the nearest integer not larger in absolute value float truncf(float x) { short ctrl1, ctrl2; float retval; __asm { fnstcw word ptr [ctrl1] movzx eax,word ptr [ctrl1] or ah,0Ch mov word ptr [ctrl2],ax fldcw word ptr [ctrl2] fld dword ptr[x] frndint fldcw word ptr [ctrl1] fstp dword ptr [retval] } return retval; } // round(): round double to nearest integer, away from zero double round(double x) { return (x > 0) ? trunc(x + 0.5) : trunc(x - 0.5); } #endif // __x86_64 #endif // _MSC_VER namespace Math { unsigned powerOfTwo(unsigned a) { // classical implementation: // unsigned res = 1; // while (a > res) res <<= 1; // return res; // optimized version a += (a == 0); // can be removed if argument is never zero return floodRight(a - 1) + 1; } void gaussian2(double& r1, double& r2) { static const double S = 2.0 / RAND_MAX; double x1, x2, w; do { x1 = S * rand() - 1.0; x2 = S * rand() - 1.0; w = x1 * x1 + x2 * x2; } while (w >= 1.0); w = sqrt((-2.0 * log(w)) / w); r1 = x1 * w; r2 = x2 * w; } } // namespace Math } // namespace openmsx openmsx-0.10.0/src/utils/unistdp.hh0000644000175000017500000000031112262345041017775 0ustar manuelmanuel00000000000000#ifndef UNISTDP_HH #define UNISTDP_HH #ifndef _MSC_VER #include #else #include #include #define getpid _getpid typedef int mode_t; #endif #endif // UNISTDP_HH openmsx-0.10.0/src/utils/TigerTree.cc0000644000175000017500000001267412262345041020206 0ustar manuelmanuel00000000000000#include "TigerTree.hh" #include "Math.hh" #include #include #include namespace openmsx { static const size_t BLOCK_SIZE = 1024; static size_t calcNumNodes(size_t dataSize) { auto numBlocks = (dataSize + BLOCK_SIZE - 1) / BLOCK_SIZE; return (numBlocks == 0) ? 1 : 2 * numBlocks - 1; } TigerTree::TigerTree(TTData& data_, size_t dataSize_) : data(data_) , dataSize(dataSize_) , hash (calcNumNodes(dataSize)) , valid(calcNumNodes(dataSize)) { memset(valid.data(), 0, valid.size()); // all invalid } const TigerHash& TigerTree::calcHash() { return calcHash(getTop()); } void TigerTree::notifyChange(size_t offset, size_t len) { assert((offset + len) <= dataSize); if (len == 0) return; valid[getTop().n] = false; // set sentinel auto first = offset / BLOCK_SIZE; auto last = (offset + len - 1) / BLOCK_SIZE; assert(first <= last); // requires len != 0 do { auto node = getLeaf(first); while (valid[node.n]) { valid[node.n] = false; node = getParent(node); } } while (++first <= last); } const TigerHash& TigerTree::calcHash(Node node) { auto n = node.n; if (!valid[n]) { if (n & 1) { // interior node auto left = getLeftChild (node); auto right = getRightChild(node); auto& h1 = calcHash(left); auto& h2 = calcHash(right); tiger_int(h1, h2, hash[n]); } else { // leaf node size_t b = n * (BLOCK_SIZE / 2); size_t l = dataSize - b; if (l >= BLOCK_SIZE) { auto* d = data.getData(b, BLOCK_SIZE); tiger_leaf(d, hash[n]); } else { // partial last block auto* d = data.getData(b, l); auto backup = d[-1]; d[-1] = 0; tiger(d - 1, l + 1, hash[n]); d[-1] = backup; } } valid[n] = true; } return hash[n]; } // The TigerTree::nodes member variable stores a linearized binary tree. The // linearization is done like in this example: // // 7 (level = 8) // ----/ \---- . // 3 11 (level = 4) // / \ / \ . // 1 5 9 | (level = 2) // / \ / \ / \ | . // 0 2 4 6 8 10 12 (level = 1) // // All leaf nodes are given even node values (0, 2, 4, .., 12). Leaf nodes have // level=1. At the next level (level=2) leaf nodes are pair-wise combined in // internal nodes. So nodes 0 and 2 are combined in node 1, 4+6->5 and 8+10->9. // Leaf-node 12 cannot be paired (there is no leaf-node 14), so there's no // corresponding node at level=2. The next level is level=4 (level values // double for each higher level). At level=4 node 3 is the combination of 1 and // 5 and 9+12->11. Note that 11 is a combination of two nodes from a different // (lower) level. And finally, node 7 at level=8 combines 3+11. // // The following methods navigate in this tree. TigerTree::Node TigerTree::getTop() const { auto n = Math::floodRight(valid.size() / 2); return Node(n, n + 1); } TigerTree::Node TigerTree::getLeaf(size_t block) const { assert((2 * block) < valid.size()); return Node(2 * block, 1); } TigerTree::Node TigerTree::getParent(Node node) const { assert(node.n < valid.size()); do { node.n = (node.n & ~(2 * node.l)) + node.l; node.l *= 2; } while (node.n >= valid.size()); return node; } TigerTree::Node TigerTree::getLeftChild(Node node) const { assert(node.n < valid.size()); assert(node.l > 1); node.l /= 2; node.n -= node.l; return node; } TigerTree::Node TigerTree::getRightChild(Node node) const { assert(node.n < valid.size()); while (1) { assert(node.l > 1); node.l /= 2; auto r = node.n + node.l; if (r < valid.size()) return Node(r, node.l); } } } // namespace openmsx #if 0 // Unittest class TTTestData : public openmsx::TTData { public: virtual uint8_t* getData(size_t offset, size_t size) { return buffer + offset; } uint8_t* buffer; }; int main() { uint8_t buffer_[8192 + 1]; uint8_t* buffer = buffer_ + 1; TTTestData data; data.buffer = buffer; // zero sized buffer openmsx::TigerTree tt0(data, 0); assert(tt0.calcHash().toString() == "LWPNACQDBZRYXW3VHJVCJ64QBZNGHOHHHZWCLNQ"); // size less than one block openmsx::TigerTree tt1(data, 100); memset(buffer, 0, 100); assert(tt1.calcHash().toString() == "EOIEKIQO6BSNCNRX2UB2MB466INV6LICZ6MPUWQ"); memset(buffer + 20, 1, 10); tt1.notifyChange(20, 10); assert(tt1.calcHash().toString() == "GOTZVYW3WIE37XFCDOY66PLLXWGP6DPN3CQRHWA"); // 3 full and one partial block openmsx::TigerTree tt2(data, 4000); memset(buffer, 0, 4000); assert(tt2.calcHash().toString() == "YC44NFWFCN3QWFRSS6ICGUJDLH7F654RCKVT7VY"); memset(buffer + 1500, 1, 10); tt2.notifyChange(1500, 10); // change a single block assert(tt2.calcHash().toString() == "JU5RYR446PVZSPMOJML4IQ2FXLDDKE522CEYIBA"); memset(buffer + 2000, 1, 100); tt2.notifyChange(2000, 100); // change two blocks assert(tt2.calcHash().toString() == "IPV53CDVB2I63HXIXVK2OUPNS26YB7V7G2Y7XIA"); // 7 full blocks (unbalanced internal binary tree) openmsx::TigerTree tt3(data, 7 * 1024); memset(buffer, 0, 7 * 1024); assert(tt3.calcHash().toString() == "FPSZ35773WS4WGBVXM255KWNETQZXMTEJGFMLTA"); memset(buffer + 512, 1, 512); tt3.notifyChange(512, 512); // part of block-0 assert(tt3.calcHash().toString() == "Z32BC2WSHPW5DYUSNSZGLDIFTEIP3DBFJ7MG2MQ"); memset(buffer + 3*1024, 1, 4*1024); tt3.notifyChange(3*1024, 4*1024); // blocks 3-6 assert(tt3.calcHash().toString() == "SJUYB3QVIJXNKZMSQZGIMHA7GA2MYU2UECDA26A"); } #endif openmsx-0.10.0/src/utils/AlignedBuffer.hh0000644000175000017500000000634412262345041021020 0ustar manuelmanuel00000000000000#ifndef ALIGNEDBUFFER_HH #define ALIGNEDBUFFER_HH #include "alignof.hh" #include #include #include #include namespace openmsx { // Interface for an aligned buffer. // This type doesn't itself provide any storage, it only 'refers' to some // storage. In that sense it is very similar in usage to a 'uint8_t*'. // // For example: // void f1(uint8_t* buf) { // ... uint8_t x = buf[7]; // ... buf[4] = 10; // ... uint8_t* begin = buf; // ... uint8_t* end = buf + 12; // } // void f2(AlignedBuffer& buf) { // // The exact same syntax as above, the only difference is that here // // the compiler knows at compile-time the alignment of 'buf', while // // above the compiler must assume worst case alignment. // } #ifdef _MSC_VER // TODO in the future use the c++11 'alignas' feature __declspec (align(16)) #endif class AlignedBuffer { public: static const size_t ALIGNMENT = 16; operator uint8_t*() { return p(); } operator const uint8_t*() const { return p(); } uint8_t* operator+(ptrdiff_t i) { return p() + i; }; const uint8_t* operator+(ptrdiff_t i) const { return p() + i; }; uint8_t& operator[](int i) { return *(p() + i); } const uint8_t& operator[](int i) const { return *(p() + i); } uint8_t& operator[](unsigned int i) { return *(p() + i); } const uint8_t& operator[](unsigned int i) const { return *(p() + i); } uint8_t& operator[](long i) { return *(p() + i); } const uint8_t& operator[](long i) const { return *(p() + i); } uint8_t& operator[](unsigned long i) { return *(p() + i); } const uint8_t& operator[](unsigned long i) const { return *(p() + i); } private: uint8_t* p() { return reinterpret_cast< uint8_t*>(this); } const uint8_t* p() const { return reinterpret_cast(this); } } #ifndef _MSC_VER __attribute__((__aligned__((16)))) #endif ; static_assert(ALIGNOF(AlignedBuffer) == AlignedBuffer::ALIGNMENT, "must be aligned"); // Provide actual storage for the AlignedBuffer // A possible alternative is to use a union. template class AlignedByteArray : public AlignedBuffer { public: size_t size() const { return N; } uint8_t* data() { return dat; } const uint8_t* data() const { return dat; } private: uint8_t dat[N]; } // Repeat alignment because Clang 3.2svn does not inherit it from an empty // base class. #ifndef _MSC_VER __attribute__((__aligned__((16)))) #endif ; static_assert(ALIGNOF(AlignedByteArray<13>) == AlignedBuffer::ALIGNMENT, "must be aligned"); static_assert(sizeof(AlignedByteArray<32>) == 32, "we rely on the empty-base optimization"); /** Cast one pointer type to another pointer type. * When asserts are enabled this checks whether the original pointer is * properly aligned to point to the destination type. */ template static inline T aligned_cast(void* p) { static_assert(std::is_pointer::value, "can only perform aligned_cast on pointers"); assert((reinterpret_cast(p) % ALIGNOF(typename std::remove_pointer::type)) == 0); return reinterpret_cast(p); } } //namespace openmsx #endif openmsx-0.10.0/src/utils/DivModBySame.hh0000644000175000017500000001641212262345041020603 0ustar manuelmanuel00000000000000#ifndef DIVISIONBYCONST_HH #define DIVISIONBYCONST_HH #include "build-info.hh" #include #include namespace openmsx { /** Helper class to divide multiple times by the same number. * Binary division can be performed by: * - multiplication by a magic number * - followed by an addition of a magic number * - follwed by a right shift over some magic number of bits * These magic constants only depend on the divisor, but they are quite * expensive to calculate. * However if you know you will divide many times by the same number this * algorithm does make sense and can result in a big speedup, especially on * CPUs which lack a hardware division instruction (like ARM). * * If the divisor is a compile-time constant, it's even faster to use * the DivModByConst utility class. */ class DivModBySame { public: void setDivisor(uint32_t divisor); inline uint32_t getDivisor() const { return divisor; } uint32_t div(uint64_t dividend) const { #if defined __x86_64 && !defined _MSC_VER uint64_t t = (__uint128_t(dividend) * m + a) >> 64; return t >> s; #elif ASM_X86_32 uint32_t _ch_ = a >> 32; uint32_t _cl_ = uint32_t(a); const uint32_t _ah_ = dividend >> 32; const uint32_t _al_ = uint32_t(dividend); const uint32_t _bh_ = m >> 32; const uint32_t _bl_ = uint32_t(m); #ifdef _MSC_VER uint32_t _s_ = s, result; __asm { // It's worth noting that simple benchmarks show this to be // no faster than straight division on an Intel E8400 // // eax and edx are clobbered by mul // eax = _ah_ // ebx is _cl_ // ecx is _tl_ - not initialized // edi is _th_ - not initialized // esi is _ch_ mov eax,_ah_ mov esi,_ch_ mov ebx,_cl_ mul _bl_ mov ecx,eax mov edi,edx mov eax,_al_ mul _bl_ add ebx,eax adc esi,edx adc edi,0 add ecx,esi adc edi,0 // eax = _ah_ // ecx is now free - use it for _bh_ mov ecx,_bh_ mov eax,_ah_ mul ecx mov ebx,eax mov esi,edx mov eax,_al_ mul ecx add ecx,eax adc edi,edx adc esi,0 add ebx,edi adc esi,0 mov cl,byte ptr [_s_] shrd ebx,esi,cl mov result,ebx } #ifdef DEBUG uint32_t checkResult = divinC(dividend); assert(checkResult == result); #endif return result; #else // Split in 3 asm sections to be able to satisfy operand // constraints: between two sections gcc can reassign operands // to different registers or memory locations. Apparently there // are only 3 free registers available on OSX (devel flavour). uint32_t _th_, _tl_; uint32_t dummy; asm ( "mull %[BL]\n\t" // eax = [AH] "movl %%eax,%[TL]\n\t" "movl %%edx,%[TH]\n\t" "movl %[AL],%%eax\n\t" "mull %[BL]\n\t" "addl %%eax,%[CL]\n\t" "adcl %%edx,%[CH]\n\t" "adcl $0,%[TH]\n\t" "addl %[CH],%[TL]\n\t" "adcl $0,%[TH]\n\t" : [TH] "=&rm" (_th_) , [TL] "=&r" (_tl_) , [CH] "=rm" (_ch_) , [CL] "=rm" (_cl_) , [EAX] "=&a" (dummy) : "[CH]" (_ch_) , "[CL]" (_cl_) , "[EAX]" (_ah_) , [AL] "m" (_al_) , [BL] "m" (_bl_) : "edx" ); asm ( "mull %[BH]\n\t" // eax = [AH] "movl %%eax,%[CL]\n\t" "movl %%edx,%[CH]\n\t" "movl %[AL],%%eax\n\t" "mull %[BH]\n\t" "addl %%eax,%[TL]\n\t" "adcl %%edx,%[TH]\n\t" "adcl $0,%[CH]\n\t" "addl %[TH],%[CL]\n\t" "adcl $0,%[CH]\n\t" : [CH] "=rm" (_ch_) , [CL] "=r" (_cl_) , [TH] "=&rm" (_th_) , [TL] "=&rm" (_tl_) , [EAX] "=&a" (dummy) : "[TH]" (_th_) , "[TL]" (_tl_) , "[EAX]" (_ah_) , [AL] "m" (_al_) , [BH] "m" (_bh_) : "edx" ); asm ( "shrd %%cl,%[CH],%[CL]\n\t" // SH = ecx : [CL] "=rm" (_cl_) : [CH] "r" (_ch_) , "[CL]" (_cl_) , [SH] "c" (s) ); return _cl_; #endif #elif defined(__arm__) uint32_t res; uint32_t dummy; asm volatile ( "ldmia %[RES],{r3,r4,r5,r6,r8}\n\t" "umull %[T],%[RES],%[AL],r3\n\t" // RES:T = AL * BL "adds %[T],%[T],r5\n\t" // T += CL "adcs %[RES],%[RES],r6\n\t" // RES += CH "mov r5,#0\n\t" "adc %[T],r5,r5\n\t" // T = carry "umlal %[RES],%[T],%[AH],r3\n\t" // T:RES = AH:AL * BL + CH:CL = TH:TL "umull r3,r6,%[AL],r4\n\t" // r6:r3 = AL * BH "adds r3,r3,%[RES]\n\t" // r3 += TL "adcs r6,r6,%[T]\n\t" // r6 += TH "adc r3,r5,r5\n\t" // r3 = carry "umlal r6,r3,%[AH],r4\n\t" // r3:r6 = AH:AL * BH:BL + CH:CL "rsb %[T],r8,#32\n\t" "lsr %[RES],r6,r8\n\t" //"orr %[RES],%[RES],r3,LSL %[T]\n\t" // not thumb2 "lsls r3,r3,%[T]\n\t" "orrs %[RES],%[RES],r3\n\t" //"mov %[AL],r3,LSR r8\n\t" // high word of result, should be 0 : [RES] "=r" (res) , [T] "=&r" (dummy) : "[RES]" (this) , [AL] "r" (uint32_t(dividend)) , [AH] "r" (dividend >> 32) : "r3","r4","r5","r6","r8" ); return res; #else return divinC(dividend); #endif } inline uint32_t divinC(uint64_t dividend) const { uint64_t t1 = uint64_t(uint32_t(dividend)) * uint32_t(m); uint64_t t2 = (dividend >> 32) * uint32_t(m); uint64_t t3 = uint32_t(dividend) * (m >> 32); uint64_t t4 = (dividend >> 32) * (m >> 32); uint64_t s1 = uint64_t(uint32_t(a)) + uint32_t(t1); uint64_t s2 = (s1 >> 32) + (a >> 32) + (t1 >> 32) + t2; uint64_t s3 = uint64_t(uint32_t(s2)) + uint32_t(t3); uint64_t s4 = (s3 >> 32) + (s2 >> 32) + (t3 >> 32) + t4; uint64_t result = s4 >> s; #ifdef DEBUG // we don't even want this overhead in devel builds assert(result == uint32_t(result)); #endif return uint32_t(result); } uint32_t mod(uint64_t dividend) const { #ifdef __arm__ uint32_t res; uint32_t dummy; asm volatile ( "ldmia %[RES],{r3,r4,r5,r6,r8,r9}\n\t" "umull %[T],%[RES],%[AL],r3\n\t" // RES:T = AL * BL "adds %[T],%[T],r5\n\t" // T += CL "adcs %[RES],%[RES],r6\n\t" // RES += CH "mov r5,#0\n\t" "adc %[T],r5,r5\n\t" // T = carry "umlal %[RES],%[T],%[AH],r3\n\t" // T:RES = AH:AL * BL + CH:CL = TH:TL "umull r3,r6,%[AL],r4\n\t" // r6:r3 = AL * BH "adds r3,r3,%[RES]\n\t" // r3 += TL "adcs r6,r6,%[T]\n\t" // r6 += TH "adc r3,r5,r5\n\t" // r3 = carry "umlal r6,r3,%[AH],r4\n\t" // r3:r6 = AH:AL * BH:BL + CH:CL "rsb %[T],r8,#32\n\t" "lsr %[RES],r6,r8\n\t" //"orr %[RES],%[RES],r3,LSL %[T]\n\t" // not thumb2 "lsls r3,r3,%[T]\n\t" "orrs %[RES],%[RES],r3\n\t" // RES = quotient (must fit in 32-bit) "mul %[AH],%[RES],r9\n\t" // AH = q * divisor "sub %[RES],%[AL],%[AH]\n\t" // RES = D - q*d : [RES] "=r" (res) , [T] "=&r" (dummy) : "[RES]" (this) , [AL] "r" (uint32_t(dividend)) , [AH] "r" (dividend >> 32) : "r3","r4","r5","r6","r8","r9" ); return res; #else assert(uint32_t(divisor) == divisor); // must fit in 32-bit uint64_t q = div(dividend); assert(uint32_t(q) == q); // must fit in 32 bit // result fits in 32-bit, so no 64-bit calculations required return uint32_t(dividend) - uint32_t(q) * uint32_t(divisor); #endif } private: // note: order is important for ARM asm routines uint64_t m; uint64_t a; uint32_t s; uint32_t divisor; // only used by mod() and getDivisor() }; } // namespace openmsx #endif // DIVISIONBYCONST_HH openmsx-0.10.0/src/utils/unreachable.hh0000644000175000017500000000404112262345041020564 0ustar manuelmanuel00000000000000#ifndef UNREACHABLE_HH #define UNREACHABLE_HH // GCC targeting MIPS will generate bad code when marking certain code as // unreachable. This is very noticeable in SDLVideoSystem::getWindowSize(), // which does not seem to write the output arguments, making openMSX fail the // creation of an SDL video mode since the requested size is rubbish. // // This compiler bug has been reported as: // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51861 // // It turns out this was not a specific issue for MIPS, but it could affect all // architectures which use branch delay slots (e.g. also AVR and SPARC). // // At this moment the bug in gcc has been fixed, but the most recent gcc-4.5.x // and gcc-4.6.x releases still contain the bug. // // For openMSX, this UNREACHABLE-optimization is not very important (it does // improve speed/code-size, but not in very critical locations). So we decided // to only enable this optimization starting from gcc-4.7 or from gcc-4.5 for a // few white-listed architectures that we know don't use delay slots. // Clang has a very convenient way of testing features, unfortunately (for now) // it's clang-only so add a fallback for non-clang compilers. #ifndef __has_builtin #define __has_builtin(x) 0 #endif #if defined(NDEBUG) // clang #if __has_builtin(__builtin_unreachable) #define UNREACHABLE __builtin_unreachable() // __builtin_unreachable() was introduced in gcc-4.5 #elif ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7))) // gcc-4.7 or above #define UNREACHABLE __builtin_unreachable() #elif ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 5) && (defined(__i386__) || defined(__x86_64__) || defined(__arm__))) // gcc-4.5 or gcc-4.6 on x86, x86_64 or arm (all without delay slots) #define UNREACHABLE __builtin_unreachable() #elif defined(_MSC_VER) // visual studio #define UNREACHABLE __assume(0) #else // fall-back #define UNREACHABLE /*nothing*/ #endif #else // asserts enabled #include #define UNREACHABLE assert(false) #endif #endif // UNREACHABLE_HH openmsx-0.10.0/src/utils/CircularBuffer.hh0000644000175000017500000000257612262345041021224 0ustar manuelmanuel00000000000000#ifndef CIRCULARBUFFER_HH #define CIRCULARBUFFER_HH #include namespace openmsx { template class CircularBuffer { public: CircularBuffer() : first(0), last(0) { } void addFront(const T& element) { assert(!isFull()); first = prev(first); buffer[first] = element; } void addBack(const T& element) { assert(!isFull()); buffer[last] = element; last = next(last); } T& removeFront() { assert(!isEmpty()); auto tmp = first; first = next(first); return buffer[tmp]; } T& removeBack() { assert(!isEmpty()); last = prev(last); return buffer[last]; } T& operator[](size_t pos) { assert(pos < MAXSIZE); auto tmp = first + pos; if (tmp > MAXSIZE) { tmp -= (MAXSIZE + 1); } return buffer[tmp]; } const T& operator[](size_t pos) const { return const_cast(*this)[pos]; } bool isEmpty() const { return (first == last); } bool isFull() const { return (first == next(last)); } size_t size() const { if (first > last) { return MAXSIZE + 1 - first + last; } else { return last - first; } } private: inline size_t next(size_t a) const { return (a != MAXSIZE) ? a + 1 : 0; } inline size_t prev(size_t a) const { return (a != 0) ? a - 1 : MAXSIZE; } size_t first, last; // one extra to be able to distinguish full and empty T buffer[MAXSIZE + 1]; }; } // namespace openmsx #endif openmsx-0.10.0/src/utils/tiger.hh0000644000175000017500000000427512262345041017436 0ustar manuelmanuel00000000000000/** * The implementation of the tiger() function is based on the corresponding * function in the tigertree project: * https://sourceforge.net/projects/tigertree/ * Which in turn states that the code is almost a literal copy from the * original Tiger authors code. It can be found at: * http://www.cs.technion.ac.il/~biham/Reports/Tiger * * The functions tiger_int() and tiger_leaf() are implemented by me. These are * built on top of the (internal) tiger_compress() function. These 2 functions * are faster than the generic tiger() function for the specific case of * tiger-tree calculations. */ #ifndef TIGER_H #define TIGER_H #include #include namespace openmsx { /** This struct represents the result of a tiger-hash. * This is a 192-bit value. It can be formatted as a 39-char base32-encoded * string. Similar to the output of the unix 'tthsum' tool. */ struct TigerHash { std::string toString() const; union { uint64_t h64[3]; uint8_t h8[24]; }; }; /** Generic function to calculate a tiger-hash. * input: start-address and size of the block * output: 192-bit hash value * The result is endianness-neutral (on big-endian systems the result is * converted so that, when interpreted as a byte-array, it is identical to * the result obtained on a little-endian system). */ void tiger(const uint8_t* str, size_t length, TigerHash& result); /** Use for tiger-tree internal node hash calculations. * Combine two earlier calculated tiger hash values in a specific way (add * marker/padding/length bytes before/after) and calculate a new hash value. * This function is not reentrant. */ void tiger_int(const TigerHash& h0, const TigerHash& h1, TigerHash& result); /** Use for tiger-tree leaf node hash calculations. * Take a 1024-byte input block, add some marker/padding/length bytes * before/after and calculate a tiger-hash. * This function is not reentrant. * This function requires that data[-1] can be (temporarily) overridden (so * after the function returns the data buffer is unchanged, but temporarily * it is changed, hence the parameter cannot be const). */ void tiger_leaf(/*const*/ uint8_t data[1024], TigerHash& result); } // namespace openmsx #endif openmsx-0.10.0/src/MSXVictorHC9xSystemControl.cc0000644000175000017500000000352612262345041022250 0ustar manuelmanuel00000000000000#include "MSXVictorHC9xSystemControl.hh" #include "serialize.hh" #include // This implementation is documented in the HC-95 service manual: // // System control: // 7FFD I/O bit 0 NMI CONTROL "0" ENABLE // I/O bit 1 INT CONTROL "1" ENABLE // I/O bit 2 WAIT CONTROL "0" ENABLE // I/O bit 3 JVC MODE ACK FLAG "1" JVC ACK // I/O bit 4 FDC DREQ CONTROL "1" ENABLE // I/O bit 5 RS-232C FLAG "1" ENABLE // I bit 6 TURBO MODE FLAG "1" TURBO // I bit 7 JVC MODE FLAG "0" JVC MODE // turbo mode is determined by a physical switch on the machine (hence readonly) namespace openmsx { MSXVictorHC9xSystemControl::MSXVictorHC9xSystemControl(const DeviceConfig& config) : MSXDevice(config) { systemControlRegister = 0x80; } MSXVictorHC9xSystemControl::~MSXVictorHC9xSystemControl() { } byte MSXVictorHC9xSystemControl::readMem(word address, EmuTime::param time) { return peekMem(address, time); } byte MSXVictorHC9xSystemControl::peekMem(word address, EmuTime::param /*time*/) const { (void)address; // avoid warning for non-assert compiles assert (address == 0x7FFD); return systemControlRegister; } void MSXVictorHC9xSystemControl::writeMem(word address, byte value, EmuTime::param /*time*/) { (void)address; // avoid warning for non-assert compiles assert (address == 0x7FFD); systemControlRegister = (value & 0x3F) | (0x80); } template void MSXVictorHC9xSystemControl::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("systemControlRegister", systemControlRegister); } INSTANTIATE_SERIALIZE_METHODS(MSXVictorHC9xSystemControl); REGISTER_MSXDEVICE(MSXVictorHC9xSystemControl, "VictorHC9xSystemControl"); } // namespace openmsx openmsx-0.10.0/src/node.mk0000644000175000017500000000245212262345041016114 0ustar manuelmanuel00000000000000include build/node-start.mk SUBDIRS:= \ cassette commands config console cpu debugger events fdc file ide \ input laserdisc memory resource security serial settings sound thread \ utils video SRC_HDR:= \ EmuTime EmuDuration DynamicClock \ MSXDevice \ GlobalSettings \ ThrottleManager \ MSXMotherBoard \ MSXPPI I8255 \ DeviceFactory \ Reactor Scheduler Schedulable \ SaveStateCLI \ MSXE6Timer \ MSXResetStatusRegister \ MSXTurboRPause \ MSXS1985 \ MSXS1990 \ DummyDevice \ MSXPrinterPort \ PrinterPortDevice DummyPrinterPortDevice \ PrinterPortSimpl PrinterPortLogger Printer \ MSXKanji MSXKanji12 MSXBunsetsu \ MSXRTC RP5C01 PasswordCart \ RealTime \ CartridgeSlotManager \ MSXDeviceSwitch MSXSwitchedDevice \ MSXMatsushita \ MSXVictorHC9xSystemControl \ MSXCielTurbo \ FirmwareSwitch \ CommandLineParser CLIOption \ CliExtension \ PluggingController Connector Pluggable PluggableFactory \ DebugDevice \ Autofire RenShaTurbo \ EmptyPatch IPSPatch \ Version \ LedStatus \ serialize serialize_meta serialize_core openmsx \ AndroidApiWrapper \ ReverseManager ReplayCLI \ MSXException SRC_ONLY:= \ main HDR_ONLY:= \ openmsx \ Clock \ InitException PlugException \ PatchInterface I8255Interface \ serialize_stl serialize_constr DIST:= \ Doxyfile include build/node-end.mk openmsx-0.10.0/src/Reactor.hh0000644000175000017500000001370612262345041016562 0ustar manuelmanuel00000000000000#ifndef REACTOR_HH #define REACTOR_HH #include "Observer.hh" #include "EventListener.hh" #include "Semaphore.hh" #include "noncopyable.hh" #include "string_ref.hh" #include "openmsx.hh" #include #include #include namespace openmsx { class EventDistributor; class CommandController; class InfoCommand; class GlobalCliComm; class GlobalCommandController; class GlobalSettings; class CliComm; class Display; class Mixer; class InputEventGenerator; class DiskFactory; class DiskManipulator; class DiskChanger; class FilePool; class UserSettings; class RomDatabase; class TclCallbackMessages; class BooleanSetting; class MSXMotherBoard; class Setting; class CommandLineParser; class AfterCommand; class QuitCommand; class MessageCommand; class MachineCommand; class TestMachineCommand; class CreateMachineCommand; class DeleteMachineCommand; class ListMachinesCommand; class ActivateMachineCommand; class StoreMachineCommand; class RestoreMachineCommand; class AviRecorder; class ConfigInfo; class RealTimeInfo; class GlobalSettings; class PollEventGenerator; template class EnumSetting; /** * Contains the main loop of openMSX. * openMSX is almost single threaded: the main thread does most of the work, * we create additional threads only if we need blocking calls for * communicating with peripherals. * This class serializes all incoming requests so they can be handled by the * main thread. */ class Reactor : private Observer, private EventListener, private noncopyable { public: Reactor(); void init(); ~Reactor(); /** * Main loop. */ void run(CommandLineParser& parser); void enterMainLoop(); void pollNow(); EventDistributor& getEventDistributor(); GlobalCliComm& getGlobalCliComm(); GlobalCommandController& getGlobalCommandController(); InputEventGenerator& getInputEventGenerator(); Display& getDisplay(); Mixer& getMixer(); DiskFactory& getDiskFactory(); DiskManipulator& getDiskManipulator(); EnumSetting& getMachineSetting(); RomDatabase& getSoftwareDatabase(); FilePool& getFilePool(); void switchMachine(const std::string& machine); MSXMotherBoard* getMotherBoard() const; static std::vector getHwConfigs(string_ref type); void block(); void unblock(); // convenience methods GlobalSettings& getGlobalSettings(); InfoCommand& getOpenMSXInfoCommand(); CommandController& getCommandController(); CliComm& getCliComm(); std::string getMachineID() const; typedef std::unique_ptr Board; Board createEmptyMotherBoard(); void replaceBoard(MSXMotherBoard& oldBoard, Board newBoard); // for reverse private: typedef std::vector Boards; void createMachineSetting(); void switchBoard(MSXMotherBoard* newBoard); void deleteBoard(MSXMotherBoard* board); MSXMotherBoard& getMachine(const std::string& machineID) const; std::vector getMachineIDs() const; // Observer virtual void update(const Setting& setting); // EventListener virtual int signalEvent(const std::shared_ptr& event); void unpause(); void pause(); Semaphore mbSem; // this should come first, because it's still used by // the destructors of the unique_ptr below // note: order of unique_ptr's is important std::unique_ptr eventDistributor; std::unique_ptr globalCliComm; std::unique_ptr globalCommandController; std::unique_ptr globalSettings; std::unique_ptr inputEventGenerator; #if UNIQUE_PTR_BUG // see openmsx.hh std::unique_ptr display2; Display* display; #else std::unique_ptr display; #endif std::unique_ptr mixer; std::unique_ptr diskFactory; std::unique_ptr diskManipulator; std::unique_ptr virtualDrive; std::unique_ptr filePool; std::unique_ptr> machineSetting; std::unique_ptr userSettings; std::unique_ptr softwareDatabase; std::unique_ptr afterCommand; std::unique_ptr quitCommand; std::unique_ptr messageCommand; std::unique_ptr machineCommand; std::unique_ptr testMachineCommand; std::unique_ptr createMachineCommand; std::unique_ptr deleteMachineCommand; std::unique_ptr listMachinesCommand; std::unique_ptr activateMachineCommand; std::unique_ptr storeMachineCommand; std::unique_ptr restoreMachineCommand; std::unique_ptr aviRecordCommand; std::unique_ptr extensionInfo; std::unique_ptr machineInfo; std::unique_ptr realTimeInfo; std::unique_ptr tclCallbackMessages; std::unique_ptr pollEventGenerator; // Locking rules for activeBoard access: // - main thread can always access activeBoard without taking a lock // - changing activeBoard handle can only be done in the main thread // and needs to take the mbSem lock // - non-main thread can only access activeBoard via specific // member functions (atm only via enterMainLoop()), it needs to take // the mbSem lock Boards boards; Boards garbageBoards; MSXMotherBoard* activeBoard; // either nullptr or a board inside 'boards' int blockedCounter; bool paused; /** * True iff the Reactor should keep running. * When this is set to false, the Reactor will end the main loop after * finishing the pending request(s). */ bool running; bool isInit; // has the init() method been run successfully friend class MachineCommand; friend class TestMachineCommand; friend class CreateMachineCommand; friend class DeleteMachineCommand; friend class ListMachinesCommand; friend class ActivateMachineCommand; friend class StoreMachineCommand; friend class RestoreMachineCommand; }; } // namespace openmsx #endif // REACTOR_HH openmsx-0.10.0/src/serialize_constr.hh0000644000175000017500000000272012262345041020534 0ustar manuelmanuel00000000000000#ifndef SERIALIZE_CONSTR_HH #define SERIALIZE_CONSTR_HH #include namespace openmsx { /** Serialize (local) constructor arguments. * * Some classes don't have a default constructor. To be able to create new * instances of such classes, we need to invoke the constructor with some * parameters (see also Creator utility class above). * * SerializeConstructorArgs can be specialized for classes that need such extra * constructor arguments. * * This only stores the 'local' constructor arguments, this means the arguments * that are specific per instance. There can also be 'global' args, these are * known in the context where the class is being loaded (so these don't need to * be stored in the archive). See below for more details on global constr args. * * The serialize_as_enum class has the following members: * typedef tuple<...> type * Tuple that holds the result of load() (see below) * void save(Archive& ar, const T& t) * This method should store the constructor args in the given archive * type load(Archive& ar, unsigned version) * This method should load the args from the given archive and return * them in a tuple. */ template struct SerializeConstructorArgs { typedef std::tuple<> type; template void save(Archive& /*ar*/, const T& /*t*/) { } template type load(Archive& /*ar*/, unsigned /*version*/) { return std::make_tuple(); } }; } // namespace openmsx #endif openmsx-0.10.0/src/Scheduler.hh0000644000175000017500000000612212262345041017073 0ustar manuelmanuel00000000000000#ifndef SCHEDULER_HH #define SCHEDULER_HH #include "EmuTime.hh" #include "likely.hh" #include "noncopyable.hh" #include namespace openmsx { class Schedulable; class MSXCPU; class SynchronizationPoint { public: SynchronizationPoint(EmuTime::param time, Schedulable* dev, int usrdat) : timeStamp(time), device(dev), userData(usrdat) {} SynchronizationPoint() : timeStamp(EmuTime::zero), device(nullptr), userData(0) {} EmuTime::param getTime() const { return timeStamp; } Schedulable* getDevice() const { return device; } int getUserData() const { return userData; } template void serialize(Archive& ar, unsigned version); private: EmuTime timeStamp; Schedulable* device; int userData; }; class Scheduler : private noncopyable { public: typedef std::vector SyncPoints; Scheduler(); ~Scheduler(); void setCPU(MSXCPU* cpu_) { cpu = cpu_; } /** * Get the current scheduler time. */ EmuTime::param getCurrentTime() const; /** * TODO */ inline EmuTime::param getNext() const { return syncPoints.front().getTime(); } /** * Schedule till a certain moment in time. */ inline void schedule(EmuTime::param limit) { if (unlikely(limit >= getNext())) { scheduleHelper(limit); // slow path not inlined } scheduleTime = limit; } template void serialize(Archive& ar, unsigned version); private: // -> intended for Schedulable friend class Schedulable; /** * Register a syncPoint. When the emulation reaches "timestamp", * the executeUntil() method of "device" gets called. * SyncPoints are ordered: smaller EmuTime -> scheduled * earlier. * The supplied EmuTime may not be smaller than the current CPU * time. * A device may register several syncPoints. * Optionally a "userData" parameter can be passed, this * parameter is not used by the Scheduler but it is passed to * the executeUntil() method of "device". This is useful * if you want to distinguish between several syncPoint types. * If you do not supply "userData" it is assumed to be zero. */ void setSyncPoint(EmuTime::param timestamp, Schedulable& device, int userData = 0); SyncPoints getSyncPoints(const Schedulable& device) const; /** * Removes a syncPoint of a given device that matches the given * userData. * If there is more than one match only one will be removed, * there is no guarantee that the earliest syncPoint is * removed. * Returns false <=> if there was no match (so nothing removed) */ bool removeSyncPoint(Schedulable& device, int userdata = 0); /** Remove all syncpoints for the given device. */ void removeSyncPoints(Schedulable& device); /** * Is there a pending syncPoint for this device? */ bool pendingSyncPoint(const Schedulable& device, int userdata = 0) const; private: void scheduleHelper(EmuTime::param limit); /** Vector used as heap, not a priority queue because that * doesn't allow removal of non-top element. */ SyncPoints syncPoints; EmuTime scheduleTime; MSXCPU* cpu; bool scheduleInProgress; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/0000755000175000017500000000000012262345041016137 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/events/InputEventFactory.hh0000644000175000017500000000044712262345041022116 0ustar manuelmanuel00000000000000#ifndef INPUTEVENTFACTORY_HH #define INPUTEVENTFACTORY_HH #include #include namespace openmsx { class Event; namespace InputEventFactory { typedef std::shared_ptr EventPtr; EventPtr createInputEvent(const std::string& str); } } // namespace openmsx #endif openmsx-0.10.0/src/events/EventListener.hh0000644000175000017500000000141512262345041021250 0ustar manuelmanuel00000000000000#ifndef EVENTLISTENER_HH #define EVENTLISTENER_HH #include namespace openmsx { class Event; class EventListener { public: virtual ~EventListener() {} /** * This method gets called when an event you are subscribed to occurs. * @result Must return a bitmask of EventListener priorities. When a * bit is set, this event won't be delivered to listeners with * that priority. It's only allowed/possible to block an event * for listeners with a strictly lower priority than this * listener. Returning 0 means don't block the event for any * listeners. */ virtual int signalEvent(const std::shared_ptr& event) = 0; protected: EventListener() {} }; } // namespace openmsx #endif // EVENTLISTENER_HH openmsx-0.10.0/src/events/CliConnection.cc0000644000175000017500000002713412262345041021204 0ustar manuelmanuel00000000000000// TODO: // - To avoid any possible conflicts, anything called from "run" should be // locked. // - Maybe document for each method whether it is called from the listener // thread or from the main thread? // - Unsubscribe at CliComm after stream is closed. #include "CliConnection.hh" #include "EventDistributor.hh" #include "Event.hh" #include "CommandController.hh" #include "CommandException.hh" #include "TclObject.hh" #include "XMLElement.hh" #include "checked_cast.hh" #include "cstdiop.hh" #include "unistdp.hh" #include "openmsx.hh" #include "StringOp.hh" #include #include #ifdef _WIN32 #include "SocketStreamWrapper.hh" #include "SspiNegotiateServer.hh" #endif using std::string; namespace openmsx { // class CliCommandEvent class CliCommandEvent : public Event { public: CliCommandEvent(const string& command_, const CliConnection* id_) : Event(OPENMSX_CLICOMMAND_EVENT) , command(command_), id(id_) { } const string& getCommand() const { return command; } const CliConnection* getId() const { return id; } virtual void toStringImpl(TclObject& result) const { result.addListElement("CliCmd"); result.addListElement(getCommand()); } virtual bool lessImpl(const Event& other) const { auto& otherCmdEvent = checked_cast(other); return getCommand() < otherCmdEvent.getCommand(); } private: const string command; const CliConnection* id; }; // class CliConnection CliConnection::CliConnection(CommandController& commandController_, EventDistributor& eventDistributor_) : thread(this) , commandController(commandController_) , eventDistributor(eventDistributor_) { user_data.state = START; user_data.unknownLevel = 0; user_data.object = this; memset(&sax_handler, 0, sizeof(sax_handler)); sax_handler.startElementNs = cb_start_element; sax_handler.endElementNs = cb_end_element; sax_handler.characters = cb_text; sax_handler.initialized = XML_SAX2_MAGIC; parser_context = xmlCreatePushParserCtxt( &sax_handler, &user_data, nullptr, 0, nullptr); for (int i = 0; i < CliComm::NUM_UPDATES; ++i) { updateEnabled[i] = false; } eventDistributor.registerEventListener(OPENMSX_CLICOMMAND_EVENT, *this); } CliConnection::~CliConnection() { eventDistributor.unregisterEventListener(OPENMSX_CLICOMMAND_EVENT, *this); xmlFreeParserCtxt(parser_context); } void CliConnection::setUpdateEnable(CliComm::UpdateType type, bool value) { updateEnabled[type] = value; } bool CliConnection::getUpdateEnable(CliComm::UpdateType type) const { return updateEnabled[type]; } void CliConnection::log(CliComm::LogLevel level, string_ref message) { auto levelStr = CliComm::getLevelStrings(); output(StringOp::Builder() << "" << XMLElement::XMLEscape(message.str()) << "\n"); } void CliConnection::update(CliComm::UpdateType type, string_ref machine, string_ref name, string_ref value) { if (!getUpdateEnable(type)) return; auto updateStr = CliComm::getUpdateStrings(); StringOp::Builder tmp; tmp << "' << XMLElement::XMLEscape(value.str()) << "\n"; output(tmp); } void CliConnection::startOutput() { output("\n"); } void CliConnection::start() { thread.start(); } void CliConnection::end() { output("\n"); close(); } void CliConnection::execute(const string& command) { PRT_DEBUG("CliConnection::execute: " << command); eventDistributor.distributeEvent( std::make_shared(command, this)); } static string reply(const string& message, bool status) { return StringOp::Builder() << "" << XMLElement::XMLEscape(message) << "\n"; } int CliConnection::signalEvent(const std::shared_ptr& event) { auto& commandEvent = checked_cast(*event); if (commandEvent.getId() == this) { try { string result = commandController.executeCommand( commandEvent.getCommand(), this); PRT_DEBUG("CliConnection::signalEvent result: " << result); output(reply(result, true)); } catch (CommandException& e) { string result = e.getMessage() + '\n'; output(reply(result, false)); } } return 0; } void CliConnection::cb_start_element( void* user_data, const xmlChar* localname, const xmlChar* /*prefix*/, const xmlChar* /*uri*/, int /*nb_namespaces*/, const xmlChar** /*namespaces*/, int /*nb_attributes*/, int /*nb_defaulted*/, const xmlChar** /*attrs*/ ) { auto parseState = static_cast(user_data); if (parseState->unknownLevel) { ++(parseState->unknownLevel); return; } switch (parseState->state) { case START: if (strcmp(reinterpret_cast(localname), "openmsx-control") == 0) { parseState->state = TAG_OPENMSX; } else { ++(parseState->unknownLevel); } break; case TAG_OPENMSX: if (strcmp(reinterpret_cast(localname), "command") == 0) { parseState->state = TAG_COMMAND; } else { ++(parseState->unknownLevel); } break; default: ++(parseState->unknownLevel); break; } parseState->content.clear(); } void CliConnection::cb_end_element( void* user_data, const xmlChar* /*localname*/, const xmlChar* /*prefix*/, const xmlChar* /*uri*/ ) { auto parseState = static_cast(user_data); if (parseState->unknownLevel) { --(parseState->unknownLevel); return; } switch (parseState->state) { case TAG_OPENMSX: parseState->object->end(); parseState->state = END; break; case TAG_COMMAND: parseState->object->execute(parseState->content); parseState->state = TAG_OPENMSX; break; default: break; } } void CliConnection::cb_text(void* user_data, const xmlChar* chars, int len) { auto parseState = static_cast(user_data); if (parseState->state == TAG_COMMAND) { parseState->content.append(reinterpret_cast(chars), len); } } // class StdioConnection static const int BUF_SIZE = 4096; StdioConnection::StdioConnection(CommandController& commandController, EventDistributor& eventDistributor) : CliConnection(commandController, eventDistributor) , ok(true) { startOutput(); start(); } StdioConnection::~StdioConnection() { end(); } void StdioConnection::run() { // runs in helper thread while (ok) { char buf[BUF_SIZE]; int n = read(STDIN_FILENO, buf, sizeof(buf)); if (n > 0) { xmlParseChunk(parser_context, buf, n, 0); } else if (n < 0) { close(); break; } } } void StdioConnection::output(string_ref message) { if (ok) { std::cout << message << std::flush; } } void StdioConnection::close() { // don't close stdin/out/err ok = false; } #ifdef _WIN32 // class PipeConnection // INVALID_HANDLE_VALUE is #defined as (HANDLE)(-1) // but that gives a old-style-cast warning static const HANDLE OPENMSX_INVALID_HANDLE_VALUE = reinterpret_cast(-1); PipeConnection::PipeConnection(CommandController& commandController, EventDistributor& eventDistributor, string_ref name) : CliConnection(commandController, eventDistributor) { string pipeName = "\\\\.\\pipe\\" + name; pipeHandle = CreateFileA(pipeName.c_str(), GENERIC_READ, 0, nullptr, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr); if (pipeHandle == OPENMSX_INVALID_HANDLE_VALUE) { char msg[256]; snprintf(msg, 255, "Error reopening pipefile '%s': error %u", pipeName.c_str(), unsigned(GetLastError())); throw FatalError(msg); } shutdownEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); if (!shutdownEvent) { throw FatalError(StringOp::Builder() << "Error creating shutdown event: " << GetLastError()); } startOutput(); start(); } PipeConnection::~PipeConnection() { end(); CloseHandle(shutdownEvent); } void InitOverlapped(LPOVERLAPPED overlapped) { ZeroMemory(overlapped, sizeof(*overlapped)); overlapped->hEvent = CreateEventW(nullptr, FALSE, FALSE, nullptr); if (!overlapped->hEvent) { throw FatalError(StringOp::Builder() << "Error creating overlapped event: " << GetLastError()); } } void ClearOverlapped(LPOVERLAPPED overlapped) { if (overlapped->hEvent) { CloseHandle(overlapped->hEvent); overlapped->hEvent = nullptr; } } void PipeConnection::run() { // runs in helper thread OVERLAPPED overlapped; InitOverlapped(&overlapped); HANDLE waitHandles[2] = { shutdownEvent, overlapped.hEvent }; while (pipeHandle != OPENMSX_INVALID_HANDLE_VALUE) { char buf[BUF_SIZE]; if (!ReadFile(pipeHandle, buf, BUF_SIZE, nullptr, &overlapped) && GetLastError() != ERROR_IO_PENDING) { break; // Pipe broke } DWORD wait = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE); if (wait == WAIT_OBJECT_0 + 1) { DWORD bytesRead; if (!GetOverlappedResult(pipeHandle, &overlapped, &bytesRead, TRUE)) { break; // Pipe broke } xmlParseChunk(parser_context, buf, bytesRead, 0); } else if (wait == WAIT_OBJECT_0) { break; // Shutdown } else { throw FatalError(StringOp::Builder() << "WaitForMultipleObjects returned unexpectedly: " << wait); } } ClearOverlapped(&overlapped); // We own the pipe handle, so close it here CloseHandle(pipeHandle); pipeHandle = OPENMSX_INVALID_HANDLE_VALUE; } void PipeConnection::output(string_ref message) { if (pipeHandle != OPENMSX_INVALID_HANDLE_VALUE) { std::cout << message << std::flush; } } void PipeConnection::close() { SetEvent(shutdownEvent); thread.join(); assert(pipeHandle == OPENMSX_INVALID_HANDLE_VALUE); } #endif // _WIN32 // class SocketConnection SocketConnection::SocketConnection(CommandController& commandController, EventDistributor& eventDistributor, SOCKET sd_) : CliConnection(commandController, eventDistributor) , sem(1), sd(sd_), established(false) { start(); } SocketConnection::~SocketConnection() { end(); } void SocketConnection::run() { // runs in helper thread #ifdef _WIN32 bool ok; { ScopedLock lock(sem); // Authenticate and authorize the caller SocketStreamWrapper stream(sd); SspiNegotiateServer server(stream); ok = server.Authenticate() && server.Authorize(); } if (!ok) { close(); return; } #endif // Start output element established = true; // TODO needs locking? startOutput(); // TODO is locking correct? // No need to lock in this thread because we don't write to 'sd' // and 'sd' only gets written to in this thread. while (true) { if (sd == OPENMSX_INVALID_SOCKET) return; char buf[BUF_SIZE]; int n = sock_recv(sd, buf, BUF_SIZE); if (n > 0) { xmlParseChunk(parser_context, buf, n, 0); } else if (n < 0) { close(); break; } } } void SocketConnection::output(string_ref message) { if (!established) { // TODO needs locking? // Connection isn't authorized yet (and opening tag is not // yet send). Ignore log and update messages for now. return; } const char* data = message.data(); unsigned pos = 0; size_t bytesLeft = message.size(); while (bytesLeft) { int bytesSend; { ScopedLock lock(sem); if (sd == OPENMSX_INVALID_SOCKET) return; bytesSend = sock_send(sd, &data[pos], bytesLeft); } if (bytesSend > 0) { bytesLeft -= bytesSend; pos += bytesSend; } else { close(); break; } } } void SocketConnection::close() { ScopedLock lock(sem); if (sd != OPENMSX_INVALID_SOCKET) { SOCKET _sd = sd; sd = OPENMSX_INVALID_SOCKET; sock_close(_sd); } } } // namespace openmsx openmsx-0.10.0/src/events/EventDistributor.cc0000644000175000017500000000704712262345041021772 0ustar manuelmanuel00000000000000#include "EventDistributor.hh" #include "EventListener.hh" #include "Reactor.hh" #include "Thread.hh" #include "stl.hh" #include #include using std::pair; using std::string; namespace openmsx { EventDistributor::EventDistributor(Reactor& reactor_) : reactor(reactor_) , listeners(NUM_EVENT_TYPES) , sem(1) { } void EventDistributor::registerEventListener( EventType type, EventListener& listener, Priority priority) { ScopedLock lock(sem); auto& priorityMap = listeners[type]; for (auto& p : priorityMap) { // a listener may only be registered once for each type assert(p.second != &listener); (void)p; } // insert at highest position that keeps listeners sorted on priority auto it = upper_bound(priorityMap.begin(), priorityMap.end(), priority, LessTupleElement<0>()); priorityMap.insert(it, std::make_pair(priority, &listener)); } void EventDistributor::unregisterEventListener( EventType type, EventListener& listener) { ScopedLock lock(sem); auto& priorityMap = listeners[type]; auto it = find_if(priorityMap.begin(), priorityMap.end(), [&](PriorityMap::value_type v) { return v.second == &listener; }); assert(it != priorityMap.end()); priorityMap.erase(it); } void EventDistributor::distributeEvent(const EventPtr& event) { // TODO: Implement a real solution against modifying data structure while // iterating through it. // For example, assign nullptr first and then iterate again after // delivering events to remove the nullptr values. // TODO: Is it useful to test for 0 listeners or should we just always // queue the event? assert(event); ScopedLock lock(sem); if (!listeners[event->getType()].empty()) { scheduledEvents.push_back(event); // must release lock, otherwise there's a deadlock: // thread 1: Reactor::deleteMotherBoard() // EventDistributor::unregisterEventListener() // thread 2: EventDistributor::distributeEvent() // Reactor::enterMainLoop() cond.signalAll(); lock.release(); reactor.enterMainLoop(); } } bool EventDistributor::isRegistered(EventType type, EventListener* listener) const { for (auto& p : listeners[type]) { if (p.second == listener) { return true; } } return false; } void EventDistributor::deliverEvents() { assert(Thread::isMainThread()); ScopedLock lock(sem); // It's possible that executing an event triggers scheduling of another // event. We also want to execute those secondary events. That's why // we have this while loop here. // For example the 'loadstate' command event, triggers a machine switch // event and as reaction to the latter event, AfterCommand will // unsubscribe from the ols MSXEventDistributor. This really should be // done before we exit this method. while (!scheduledEvents.empty()) { EventQueue eventsCopy; swap(eventsCopy, scheduledEvents); for (auto& event : eventsCopy) { auto type = event->getType(); auto priorityMapCopy = listeners[type]; sem.up(); unsigned blockPriority = unsigned(-1); // allow all for (auto& p : priorityMapCopy) { // It's possible delivery to one of the previous // Listeners unregistered the current Listener. if (!isRegistered(type, p.second)) continue; unsigned currentPriority = p.first; if (currentPriority >= blockPriority) break; if (unsigned block = p.second->signalEvent(event)) { assert(block > currentPriority); blockPriority = block; } } sem.down(); } } } bool EventDistributor::sleep(unsigned us) { return cond.waitTimeout(us); } } // namespace openmsx openmsx-0.10.0/src/events/Event.cc0000644000175000017500000000157512262345041017537 0ustar manuelmanuel00000000000000#include "Event.hh" #include "TclObject.hh" namespace openmsx { // class Event Event::Event(EventType type_) : type(type_) { } Event::~Event() { } EventType Event::getType() const { return type; } std::string Event::toString() const { TclObject result; toStringImpl(result); return result.getString().str(); } bool Event::operator<(const Event& other) const { return (getType() != other.getType()) ? (getType() < other.getType()) : lessImpl(other); } bool Event::operator==(const Event& other) const { return !(*this < other) && !(other < *this); } bool Event::operator!=(const Event& other) const { return !(*this == other); } void SimpleEvent::toStringImpl(TclObject& result) const { result.addListElement("simple"); result.addListElement(int(getType())); } bool SimpleEvent::lessImpl(const Event& /*other*/) const { return false; } } // namespace openmsx openmsx-0.10.0/src/events/CliServer.hh0000644000175000017500000000131012262345041020351 0ustar manuelmanuel00000000000000#ifndef CLISERVER_HH #define CLISERVER_HH #include "Thread.hh" #include "Socket.hh" #include namespace openmsx { class CommandController; class EventDistributor; class GlobalCliComm; class CliServer : private Runnable { public: CliServer(CommandController& commandController, EventDistributor& eventDistributor, GlobalCliComm& cliComm); ~CliServer(); private: // Runnable virtual void run(); void mainLoop(); void createSocket(); bool exitAcceptLoop(); CommandController& commandController; EventDistributor& eventDistributor; GlobalCliComm& cliComm; Thread thread; std::string socketName; SOCKET listenSock; bool exitLoop; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/node.mk0000644000175000017500000000056512262345041017423 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ EventDistributor \ HotKey \ AfterCommand \ Keys \ CliComm GlobalCliComm MSXCliComm \ StdioMessages TclCallbackMessages \ CliServer CliConnection Socket \ Event InputEvents \ InputEventGenerator InputEventFactory \ MessageCommand \ HDR_ONLY:= \ CliListener \ EventListener \ FinishFrameEvent include build/node-end.mk openmsx-0.10.0/src/events/CliServer.cc0000644000175000017500000001414012262345041020344 0ustar manuelmanuel00000000000000#include "CliServer.hh" #include "GlobalCliComm.hh" #include "CliConnection.hh" #include "StringOp.hh" #include "FileOperations.hh" #include "MSXException.hh" #include "memory.hh" #include "statp.hh" #include #ifdef _WIN32 #include #include #include #else #include #endif using std::string; namespace openmsx { static string getUserName() { #if defined(_WIN32) return "default"; #else struct passwd* pw = getpwuid(getuid()); return pw->pw_name ? pw->pw_name : ""; #endif } static bool checkSocketDir(const string& dir) { struct stat st; if (stat(dir.c_str(), &st)) { // cannot stat return false; } if (!S_ISDIR(st.st_mode)) { // not a directory return false; } #ifndef _WIN32 // only do permission and owner checks on *nix if ((st.st_mode & 0777) != 0700) { // wrong permissions return false; } if (st.st_uid != getuid()) { // wrong uid return false; } #endif return true; } static bool checkSocket(const string& socket) { string_ref name = FileOperations::getFilename(socket); if (!name.starts_with("socket.")) { return false; // wrong name } struct stat st; if (stat(socket.c_str(), &st)) { // cannot stat return false; } #ifdef _WIN32 if (!S_ISREG(st.st_mode)) { // not a regular file return false; } #else if (!S_ISSOCK(st.st_mode)) { // not a socket return false; } #endif #ifndef _WIN32 // only do permission and owner checks on *nix if ((st.st_mode & 0777) != 0600) { // check will be different on win32 (!= 777) thus actually useless // wrong permissions return false; } if (st.st_uid != getuid()) { // does this work on win32? is this check meaningful? // wrong uid return false; } #endif return true; } #ifdef _WIN32 static int openPort(SOCKET listenSock) { const int BASE = 9938; const int RANGE = 64; srand(unsigned(time(nullptr))); // easily predicatble, but doesn't matter int first = rand(); for (int n = 0; n < RANGE; ++n) { int port = BASE + ((first + n) % RANGE); sockaddr_in server_address; memset(&server_address, 0, sizeof(server_address)); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_LOOPBACK); server_address.sin_port = htons(port); if (bind(listenSock, reinterpret_cast(&server_address), sizeof(server_address)) != -1) { return port; } } throw MSXException("Couldn't open socket."); } #endif void CliServer::createSocket() { string dir = FileOperations::getTempDir() + "/openmsx-" + getUserName(); FileOperations::mkdir(dir, 0700); if (!checkSocketDir(dir)) { throw MSXException("Couldn't create socket directory."); } socketName = StringOp::Builder() << dir << "/socket." << int(getpid()); #ifdef _WIN32 listenSock = socket(AF_INET, SOCK_STREAM, 0); if (listenSock == OPENMSX_INVALID_SOCKET) { throw MSXException(sock_error()); } int portNumber = openPort(listenSock); // write port number to file FileOperations::unlink(socketName); // ignore error std::ofstream out; FileOperations::openofstream(out, socketName); out << portNumber << std::endl; if (!out.good()) { throw MSXException("Couldn't open socket."); } #else listenSock = socket(AF_UNIX, SOCK_STREAM, 0); if (listenSock == OPENMSX_INVALID_SOCKET) { throw MSXException(sock_error()); } FileOperations::unlink(socketName); // ignore error sockaddr_un addr; strcpy(addr.sun_path, socketName.c_str()); addr.sun_family = AF_UNIX; if (bind(listenSock, reinterpret_cast(&addr), sizeof(addr)) == -1) { throw MSXException("Couldn't open socket."); } if (chmod(socketName.c_str(), 0600) == -1) { throw MSXException("Couldn't open socket."); } #endif if (!checkSocket(socketName)) { throw MSXException("Couldn't open socket."); } listen(listenSock, SOMAXCONN); } // The BSD socket API does not contain a simple way to cancel a call to // accept(). As a workaround, we connect to the server socket ourselves. bool CliServer::exitAcceptLoop() { #ifdef _WIN32 // Windows application-level firewalls pop up a warning about openMSX // connecting to itself. Since closing the socket is sufficient to make // Windows exit the accept() call, this code is disabled on Windows. return true; /* SOCKET sd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in addr; memset((char*)&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); addr.sin_port = htons(portNumber); */ #else SOCKET sd = socket(AF_UNIX, SOCK_STREAM, 0); sockaddr_un addr; addr.sun_family = AF_UNIX; strcpy(addr.sun_path, socketName.c_str()); // Code below is OS-independent, but unreachable on Windows: if (sd == OPENMSX_INVALID_SOCKET) { return false; } int r = connect(sd, reinterpret_cast(&addr), sizeof(addr)); close(sd); return r != SOCKET_ERROR; #endif } static void deleteSocket(const string& socket) { FileOperations::unlink(socket); // ignore errors string dir = socket.substr(0, socket.find_last_of('/')); FileOperations::rmdir(dir); // ignore errors } CliServer::CliServer(CommandController& commandController_, EventDistributor& eventDistributor_, GlobalCliComm& cliComm_) : commandController(commandController_) , eventDistributor(eventDistributor_) , cliComm(cliComm_) , thread(this) , listenSock(OPENMSX_INVALID_SOCKET) { exitLoop = false; sock_startup(); try { createSocket(); thread.start(); } catch (MSXException& e) { cliComm.printWarning(e.getMessage()); } } CliServer::~CliServer() { exitLoop = true; if (listenSock != OPENMSX_INVALID_SOCKET) { sock_close(listenSock); if (!exitAcceptLoop()) { // clean exit failed, try emergency exit thread.stop(); } } thread.join(); deleteSocket(socketName); sock_cleanup(); } void CliServer::run() { mainLoop(); } void CliServer::mainLoop() { while (!exitLoop) { // wait for incomming connection SOCKET sd = accept(listenSock, nullptr, nullptr); if (sd == OPENMSX_INVALID_SOCKET) { // sock_close(listenSock); // hangs on win32 return; } CliListener* connection = new SocketConnection( commandController, eventDistributor, sd); cliComm.addListener(connection); } } } // namespace openmsx openmsx-0.10.0/src/events/TclCallbackMessages.cc0000644000175000017500000000176512262345041022306 0ustar manuelmanuel00000000000000#include "TclCallbackMessages.hh" #include "GlobalCliComm.hh" #include "TclCallback.hh" #include "memory.hh" using std::string; namespace openmsx { TclCallbackMessages::TclCallbackMessages(GlobalCliComm& cliComm_, CommandController& controller) : cliComm(cliComm_) , messageCallback(make_unique( controller, "message_callback", "Tcl proc called when a new message is available", false, // don't print callback err on cliComm (would cause infinite loop) false)) // don't save setting { cliComm.addListener(this); } TclCallbackMessages::~TclCallbackMessages() { cliComm.removeListener(this); } void TclCallbackMessages::log(CliComm::LogLevel level, string_ref message) { auto levelStr = CliComm::getLevelStrings(); messageCallback->execute(message, levelStr[level]); } void TclCallbackMessages::update( CliComm::UpdateType /*type*/, string_ref /*machine*/, string_ref /*name*/, string_ref /*value*/) { // ignore } } // namespace openmsx openmsx-0.10.0/src/events/MessageCommand.hh0000644000175000017500000000072312262345041021345 0ustar manuelmanuel00000000000000#ifndef MESSAGECOMMAND_HH #define MESSAGECOMMAND_HH #include "Command.hh" namespace openmsx { class CommandController; class MessageCommand : public Command { public: MessageCommand(CommandController& controller); virtual std::string execute(const std::vector& tokens); virtual std::string help(const std::vector& tokens) const; virtual void tabCompletion(std::vector& tokens) const; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/CliComm.hh0000644000175000017500000000235212262345041020005 0ustar manuelmanuel00000000000000#ifndef CLICOMM_HH #define CLICOMM_HH #include "array_ref.hh" #include "string_ref.hh" namespace openmsx { class CliComm { public: enum LogLevel { INFO, WARNING, LOGLEVEL_ERROR, // ERROR may give preprocessor name clashes PROGRESS, NUM_LEVELS // must be last }; enum UpdateType { LED, SETTING, SETTINGINFO, HARDWARE, PLUG, UNPLUG, MEDIA, STATUS, EXTENSION, SOUNDDEVICE, CONNECTOR, NUM_UPDATES // must be last }; virtual void log(LogLevel level, string_ref message) = 0; virtual void update(UpdateType type, string_ref name, string_ref value) = 0; // convenience methods (shortcuts for log()) void printInfo (string_ref message); void printWarning (string_ref message); void printError (string_ref message); void printProgress(string_ref message); // string representations of the LogLevel and UpdateType enums static array_ref getLevelStrings() { return make_array_ref(levelStr); } static array_ref getUpdateStrings() { return make_array_ref(updateStr); } protected: CliComm(); virtual ~CliComm(); private: static const char* const levelStr [NUM_LEVELS]; static const char* const updateStr[NUM_UPDATES]; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/InputEventGenerator.hh0000644000175000017500000000531112262345041022430 0ustar manuelmanuel00000000000000#ifndef INPUTEVENTGENERATOR_HH #define INPUTEVENTGENERATOR_HH #include "Observer.hh" #include "EventListener.hh" #include "noncopyable.hh" #include "Keys.hh" #include #include namespace openmsx { class CommandController; class BooleanSetting; class EventDistributor; class Setting; class EscapeGrabCmd; class InputEventGenerator : private Observer, private EventListener, private noncopyable { public: InputEventGenerator(CommandController& commandController, EventDistributor& eventDistributor); virtual ~InputEventGenerator(); /** Wait for event(s) and handle it. * This method should be called from the main thread. */ void wait(); /** * Enable or disable keyboard event repeats */ void setKeyRepeat(bool enable); /** * This functions shouldn't be needed, but in the SDL library input * and video or closely coupled (sigh). For example when the video mode * is changed we need to reset the keyrepeat and unicode settings. */ void reinit(); /** Input Grab on or off */ BooleanSetting& getGrabInput() const { return *grabInput; } /** Normally the following two functions simply delegate to * SDL_JoystickNumButtons() and SDL_JoystickGetButton(). Except on * Android, see comments in .cc for more details. */ static int joystickNumButtons(SDL_Joystick* joystick); static bool joystickGetButton(SDL_Joystick* joystick, int button); private: typedef std::shared_ptr EventPtr; void poll(); void handle(const SDL_Event& event); void setGrabInput(bool grab); // Observer virtual void update(const Setting& setting); // EventListener virtual int signalEvent(const std::shared_ptr& event); EventDistributor& eventDistributor; const std::unique_ptr grabInput; const std::unique_ptr escapeGrabCmd; friend class EscapeGrabCmd; enum EscapeGrabState { ESCAPE_GRAB_WAIT_CMD, ESCAPE_GRAB_WAIT_LOST, ESCAPE_GRAB_WAIT_GAIN } escapeGrabState; // OsdControl void setNewOsdControlButtonState( unsigned newState, const EventPtr& origEvent); void triggerOsdControlEventsFromJoystickAxisMotion( unsigned axis, short value, const EventPtr& origEvent); void osdControlChangeButton( bool up, unsigned changedButtonMask, const EventPtr& origEvent); void triggerOsdControlEventsFromJoystickButtonEvent( unsigned button, bool up, const EventPtr& origEvent); void triggerOsdControlEventsFromKeyEvent( Keys::KeyCode keyCode, bool up, const EventPtr& origEvent); bool keyRepeat; unsigned osdControlButtonsState; // 0 is pressed, 1 is released // only for Android static bool androidButtonA; static bool androidButtonB; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/HotKey.hh0000644000175000017500000000520412262345041017664 0ustar manuelmanuel00000000000000#ifndef HOTKEY_HH #define HOTKEY_HH #include "Event.hh" #include "EventListener.hh" #include "noncopyable.hh" #include "stl.hh" #include #include #include #include #include namespace openmsx { class GlobalCommandController; class EventDistributor; class XMLElement; class BindCmd; class UnbindCmd; class ActivateCmd; class DeactivateCmd; class AlarmEvent; class HotKey : public EventListener, private noncopyable { public: struct HotKeyInfo { HotKeyInfo() {} // for map::operator[] HotKeyInfo(const std::string& command_, bool repeat_ = false) : command(command_), repeat(repeat_) {} std::string command; bool repeat; }; typedef std::shared_ptr EventPtr; typedef std::map BindMap; typedef std::set KeySet; HotKey(GlobalCommandController& commandController, EventDistributor& eventDistributor); virtual ~HotKey(); void loadBindings(const XMLElement& config); void saveBindings(XMLElement& config) const; private: struct LayerInfo { std::string layer; bool blocking; }; void initDefaultBindings(); void bind (const EventPtr& event, const HotKeyInfo& info); void unbind (const EventPtr& event); void bindDefault (const EventPtr& event, const HotKeyInfo& info); void unbindDefault(const EventPtr& event); void bindLayer (const EventPtr& event, const HotKeyInfo& info, const std::string& layer); void unbindLayer (const EventPtr& event, const std::string& layer); void unbindFullLayer(const std::string& layer); void activateLayer (const std::string& layer, bool blocking); void deactivateLayer(const std::string& layer); void executeBinding(const EventPtr& event, const HotKeyInfo& info); void startRepeat (const EventPtr& event); void stopRepeat(); // EventListener virtual int signalEvent(const EventPtr& event); friend class BindCmd; friend class UnbindCmd; friend class ActivateCmd; friend class DeactivateCmd; const std::unique_ptr bindCmd; const std::unique_ptr unbindCmd; const std::unique_ptr bindDefaultCmd; const std::unique_ptr unbindDefaultCmd; const std::unique_ptr activateCmd; const std::unique_ptr deactivateCmd; const std::unique_ptr repeatAlarm; BindMap cmdMap; BindMap defaultMap; std::map layerMap; std::vector activeLayers; KeySet boundKeys; KeySet unboundKeys; GlobalCommandController& commandController; EventDistributor& eventDistributor; EventPtr lastEvent; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/StdioMessages.hh0000644000175000017500000000056612262345041021241 0ustar manuelmanuel00000000000000#ifndef STDIOMESSAGES_HH #define STDIOMESSAGES_HH #include "CliListener.hh" namespace openmsx { class StdioMessages : public CliListener { public: virtual void log(CliComm::LogLevel level, string_ref message); virtual void update(CliComm::UpdateType type, string_ref machine, string_ref name, string_ref value); }; } // namespace openmsx #endif openmsx-0.10.0/src/events/AfterCommand.hh0000644000175000017500000000373712262345041021032 0ustar manuelmanuel00000000000000#ifndef AFTERCOMMAND_HH #define AFTERCOMMAND_HH #include "Command.hh" #include "EventListener.hh" #include "Event.hh" #include #include namespace openmsx { class Reactor; class EventDistributor; class EventDistributor; class CommandController; class AfterCmd; class Event; class AfterCommand : public Command, private EventListener { public: typedef std::shared_ptr EventPtr; AfterCommand(Reactor& reactor, EventDistributor& eventDistributor, CommandController& commandController); virtual ~AfterCommand(); virtual void execute(const std::vector& tokens, TclObject& result); virtual std::string help(const std::vector& tokens) const; virtual void tabCompletion(std::vector& tokens) const; private: template void executeMatches(PRED pred); template void executeEvents(); template void afterEvent( const std::vector& tokens, TclObject& result); void afterInputEvent(const EventPtr& event, const std::vector& tokens, TclObject& result); void afterTclTime (int ms, const std::vector& tokens, TclObject& result); void afterTime (const std::vector& tokens, TclObject& result); void afterRealTime(const std::vector& tokens, TclObject& result); void afterIdle (const std::vector& tokens, TclObject& result); void afterInfo (const std::vector& tokens, TclObject& result); void afterCancel (const std::vector& tokens, TclObject& result); // EventListener virtual int signalEvent(const std::shared_ptr& event); typedef std::vector> AfterCmds; AfterCmds afterCmds; Reactor& reactor; EventDistributor& eventDistributor; friend class AfterCmd; friend class AfterTimedCmd; friend class AfterRealTimeCmd; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/HotKey.cc0000644000175000017500000005172112262345041017657 0ustar manuelmanuel00000000000000#include "HotKey.hh" #include "InputEventFactory.hh" #include "GlobalCommandController.hh" #include "Command.hh" #include "CommandException.hh" #include "EventDistributor.hh" #include "CliComm.hh" #include "InputEvents.hh" #include "XMLElement.hh" #include "TclObject.hh" #include "SettingsConfig.hh" #include "AlarmEvent.hh" #include "memory.hh" #include "unreachable.hh" #include "build-info.hh" #include #include using std::string; using std::vector; using std::make_shared; // This file implements all Tcl key bindings. These are the 'classical' hotkeys // (e.g. F11 to (un)mute sound) and the more recent input layers. The idea // behind an input layer is something like an OSD widget that (temporarily) // takes semi-exclusive access to the input. So while the widget is active // keyboard (and joystick) input is no longer passed to the emulated MSX. // However the classical hotkeys or the openMSX console still receive input. namespace openmsx { const bool META_HOT_KEYS = #ifdef __APPLE__ true; #else false; #endif class BindCmd : public Command { public: BindCmd(CommandController& commandController, HotKey& hotKey, bool defaultCmd); virtual string execute(const vector& tokens); virtual string help(const vector& tokens) const; private: string formatBinding(const HotKey::BindMap::value_type& p); HotKey& hotKey; const bool defaultCmd; }; class UnbindCmd : public Command { public: UnbindCmd(CommandController& commandController, HotKey& hotKey, bool defaultCmd); virtual string execute(const vector& tokens); virtual string help(const vector& tokens) const; private: HotKey& hotKey; const bool defaultCmd; }; class ActivateCmd : public Command { public: ActivateCmd(CommandController& commandController, HotKey& hotKey); virtual string execute(const vector& tokens); virtual string help(const vector& tokens) const; private: HotKey& hotKey; }; class DeactivateCmd : public Command { public: DeactivateCmd(CommandController& commandController, HotKey& hotKey); virtual string execute(const vector& tokens); virtual string help(const vector& tokens) const; private: HotKey& hotKey; }; HotKey::HotKey(GlobalCommandController& commandController_, EventDistributor& eventDistributor_) : bindCmd(make_unique( commandController_, *this, false)) , unbindCmd(make_unique( commandController_, *this, false)) , bindDefaultCmd(make_unique( commandController_, *this, true)) , unbindDefaultCmd(make_unique( commandController_, *this, true)) , activateCmd(make_unique( commandController_, *this)) , deactivateCmd(make_unique( commandController_, *this)) , repeatAlarm(make_unique( eventDistributor_, *this, OPENMSX_REPEAT_HOTKEY, EventDistributor::HOTKEY)) , commandController(commandController_) , eventDistributor(eventDistributor_) { initDefaultBindings(); eventDistributor.registerEventListener( OPENMSX_KEY_DOWN_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_KEY_UP_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_MOUSE_MOTION_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_UP_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_DOWN_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_UP_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_JOY_AXIS_MOTION_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_FOCUS_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_OSD_CONTROL_RELEASE_EVENT, *this, EventDistributor::HOTKEY); eventDistributor.registerEventListener( OPENMSX_OSD_CONTROL_PRESS_EVENT, *this, EventDistributor::HOTKEY); } HotKey::~HotKey() { eventDistributor.unregisterEventListener(OPENMSX_OSD_CONTROL_PRESS_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_OSD_CONTROL_RELEASE_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_FOCUS_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_JOY_BUTTON_UP_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_JOY_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_JOY_AXIS_MOTION_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_MOUSE_BUTTON_UP_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_MOUSE_MOTION_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_KEY_UP_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_KEY_DOWN_EVENT, *this); } void HotKey::initDefaultBindings() { // TODO move to Tcl script? if (META_HOT_KEYS) { // Hot key combos using Mac's Command key. bindDefault(make_shared( Keys::combine(Keys::K_D, Keys::KM_META)), HotKeyInfo("screenshot -guess-name")); bindDefault(make_shared( Keys::combine(Keys::K_P, Keys::KM_META)), HotKeyInfo("toggle pause")); bindDefault(make_shared( Keys::combine(Keys::K_T, Keys::KM_META)), HotKeyInfo("toggle throttle")); bindDefault(make_shared( Keys::combine(Keys::K_L, Keys::KM_META)), HotKeyInfo("toggle console")); bindDefault(make_shared( Keys::combine(Keys::K_U, Keys::KM_META)), HotKeyInfo("toggle mute")); bindDefault(make_shared( Keys::combine(Keys::K_F, Keys::KM_META)), HotKeyInfo("toggle fullscreen")); bindDefault(make_shared( Keys::combine(Keys::K_Q, Keys::KM_META)), HotKeyInfo("exit")); } else { // Hot key combos for typical PC keyboards. bindDefault(make_shared(Keys::K_PRINT), HotKeyInfo("screenshot -guess-name")); bindDefault(make_shared(Keys::K_PAUSE), HotKeyInfo("toggle pause")); bindDefault(make_shared(Keys::K_F9), HotKeyInfo("toggle throttle")); bindDefault(make_shared(Keys::K_F10), HotKeyInfo("toggle console")); bindDefault(make_shared(Keys::K_F11), HotKeyInfo("toggle mute")); bindDefault(make_shared(Keys::K_F12), HotKeyInfo("toggle fullscreen")); bindDefault(make_shared( Keys::combine(Keys::K_F4, Keys::KM_ALT)), HotKeyInfo("exit")); bindDefault(make_shared( Keys::combine(Keys::K_PAUSE, Keys::KM_CTRL)), HotKeyInfo("exit")); bindDefault(make_shared( Keys::combine(Keys::K_RETURN, Keys::KM_ALT)), HotKeyInfo("toggle fullscreen")); #if PLATFORM_ANDROID // The follwing binding is specific for Android, in order // to remap the android back button to an SDL KEY event. // I could have put all Android key bindings in a separate // else(...) clause. However, an Android user might actually // be using a PC keyboard (through USB or Bluetooth) and in such // case expect all default PC keybindings to exist as well bindDefault(make_shared(Keys::K_WORLD_92), HotKeyInfo("quitmenu::quit_menu")); #endif } } static HotKey::EventPtr createEvent(const string& str) { auto event = InputEventFactory::createInputEvent(str); if (!dynamic_cast (event.get()) && !dynamic_cast (event.get()) && !dynamic_cast(event.get()) && !dynamic_cast (event.get()) && !dynamic_cast (event.get()) && !dynamic_cast (event.get())) { throw CommandException("Unsupported event type"); } return event; } void HotKey::loadBindings(const XMLElement& config) { // restore default bindings unboundKeys.clear(); boundKeys.clear(); cmdMap.clear(); cmdMap.insert(defaultMap.begin(), defaultMap.end()); // load bindings auto* bindingsElement = config.findChild("bindings"); if (!bindingsElement) return; auto copy = *bindingsElement; // dont iterate over changing container for (auto& elem : copy.getChildren()) { try { if (elem.getName() == "bind") { bind(createEvent(elem.getAttribute("key")), HotKeyInfo(elem.getData(), elem.getAttributeAsBool("repeat", false))); } else if (elem.getName() == "unbind") { unbind(createEvent(elem.getAttribute("key"))); } } catch (MSXException& e) { commandController.getCliComm().printWarning( "Error while loading key bindings: " + e.getMessage()); } } } void HotKey::saveBindings(XMLElement& config) const { auto& bindingsElement = config.getCreateChild("bindings"); bindingsElement.removeAllChildren(); // add explicit bind's for (auto& k : boundKeys) { auto it2 = cmdMap.find(k); assert(it2 != cmdMap.end()); auto& info = it2->second; XMLElement elem("bind", info.command); elem.addAttribute("key", k->toString()); if (info.repeat) { elem.addAttribute("repeat", "true"); } bindingsElement.addChild(std::move(elem)); } // add explicit unbind's for (auto& k : unboundKeys) { XMLElement elem("unbind"); elem.addAttribute("key", k->toString()); bindingsElement.addChild(std::move(elem)); } } void HotKey::bind(const EventPtr& event, const HotKeyInfo& info) { unboundKeys.erase(event); boundKeys.insert(event); defaultMap.erase(event); cmdMap[event] = info; saveBindings(commandController.getSettingsConfig().getXMLElement()); } void HotKey::unbind(const EventPtr& event) { if (boundKeys.find(event) == boundKeys.end()) { // only when not a regular bound event unboundKeys.insert(event); } boundKeys.erase(event); defaultMap.erase(event); cmdMap.erase(event); saveBindings(commandController.getSettingsConfig().getXMLElement()); } void HotKey::bindDefault(const EventPtr& event, const HotKeyInfo& info) { if ((unboundKeys.find(event) == unboundKeys.end()) && (boundKeys.find(event) == boundKeys.end())) { // not explicity bound or unbound cmdMap[event] = info; } defaultMap[event] = info; } void HotKey::unbindDefault(const EventPtr& event) { if ((unboundKeys.find(event) == unboundKeys.end()) && (boundKeys.find(event) == boundKeys.end())) { // not explicity bound or unbound cmdMap.erase(event); } defaultMap.erase(event); } void HotKey::bindLayer(const EventPtr& event, const HotKeyInfo& info, const string& layer) { layerMap[layer][event] = info; } void HotKey::unbindLayer(const EventPtr& event, const string& layer) { layerMap[layer].erase(event); } void HotKey::unbindFullLayer(const string& layer) { layerMap.erase(layer); } void HotKey::activateLayer(const std::string& layer, bool blocking) { // Insert new activattion record at the end of the list. // (it's not an error if the same layer was already active, in such // as case it will now appear twice in the list of active layer, // and it must also be deactivated twice). LayerInfo info; info.layer = layer; info.blocking = blocking; activeLayers.push_back(info); } void HotKey::deactivateLayer(const std::string& layer) { // remove the first matching activation record from the end // (it's not an error if there is no match at all) auto it = std::find_if(activeLayers.rbegin(), activeLayers.rend(), [&](const LayerInfo& info) { return info.layer == layer; }); if (it != activeLayers.rend()) { // 'reverse_iterator' -> 'iterator' conversion is a bit tricky activeLayers.erase((it + 1).base()); } } static HotKey::BindMap::const_iterator findMatch( const HotKey::BindMap& map, const Event& event) { return find_if(map.begin(), map.end(), [&](const HotKey::BindMap::value_type& p) { return p.first->matches(event); }); } int HotKey::signalEvent(const EventPtr& event_) { // Convert special 'repeat' event into the actual to-be-repeated event. EventPtr event = event_; if (event->getType() == OPENMSX_REPEAT_HOTKEY) { if (!lastEvent) return 0; event = lastEvent; } else if (lastEvent != event) { // If the newly received event is different from the repeating // event, we stop the repeat process. // Except when we're repeating a OsdControlEvent and the // received event was actually the 'generating' event for the // Osd event. E.g. a cursor-keyboard-down event will generate // a corresponding osd event (the osd event is send before the // original event). Without this hack, key-repeat will not work // for osd key bindings. if (lastEvent && lastEvent->isRepeatStopper(*event)) { stopRepeat(); } } // First search in active layers (from back to front) bool blocking = false; for (auto it = activeLayers.rbegin(); it != activeLayers.rend(); ++it) { auto& cmap = layerMap[it->layer]; // ok, if this entry doesn't exist yet auto it2 = findMatch(cmap, *event); if (it2 != cmap.end()) { executeBinding(event, it2->second); // Deny event to MSX listeners, also don't pass event // to other layers (including the default layer). return EventDistributor::MSX; } blocking = it->blocking; if (blocking) break; // don't try lower layers } // If the event was not yet handled, try the default layer. auto it = findMatch(cmdMap, *event); if (it != cmdMap.end()) { executeBinding(event, it->second); return EventDistributor::MSX; // deny event to MSX listeners } // Event is not handled, only let it pass to the MSX if there was no // blocking layer active. return blocking ? EventDistributor::MSX : 0; } void HotKey::executeBinding(const EventPtr& event, const HotKeyInfo& info) { if (info.repeat) { startRepeat(event); } try { // ignore return value commandController.executeCommand(info.command); } catch (CommandException& e) { commandController.getCliComm().printWarning( "Error executing hot key command: " + e.getMessage()); } } void HotKey::startRepeat(const EventPtr& event) { // I initially thought about using the builtin SDL key-repeat feature, // but that won't work for example on joystick buttons. So we have to // code it ourselves. // On android, because of the sensitivity of the touch screen it's // very hard to have touches of short durations. So half a second is // too short for the key-repeat-delay. A full second should be fine. static const unsigned DELAY = PLATFORM_ANDROID ? 1000 : 500; // Repeat period. static const unsigned PERIOD = 30; unsigned delay = (lastEvent ? PERIOD : DELAY) * 1000; lastEvent = event; repeatAlarm->schedule(delay); } void HotKey::stopRepeat() { lastEvent.reset(); repeatAlarm->cancel(); } // class BindCmd static string getBindCmdName(bool defaultCmd) { return defaultCmd ? "bind_default" : "bind"; } BindCmd::BindCmd(CommandController& commandController, HotKey& hotKey_, bool defaultCmd_) : Command(commandController, getBindCmdName(defaultCmd_)) , hotKey(hotKey_) , defaultCmd(defaultCmd_) { } string BindCmd::formatBinding(const HotKey::BindMap::value_type& p) { auto& info = p.second; return p.first->toString() + (info.repeat ? " [repeat]" : "") + ": " + info.command + '\n'; } static vector parse(bool defaultCmd, vector tokens, string& layer, bool& layers) { layers = false; for (size_t i = 1; i < tokens.size(); /**/) { if (tokens[i] == "-layer") { if (i == (tokens.size() - 1)) { throw CommandException("Missing layer name"); } if (defaultCmd) { throw CommandException( "Layers are not supported for default bindings"); } layer = tokens[i + 1]; auto it = tokens.begin() + i; tokens.erase(it, it + 2); } else if (tokens[i] == "-layers") { layers = true; tokens.erase(tokens.begin() + i); } else { ++i; } } return tokens; } string BindCmd::execute(const vector& tokens_) { string layer; bool layers; auto tokens = parse(defaultCmd, tokens_, layer, layers); auto& cmdMap = defaultCmd ? hotKey.defaultMap : layer.empty() ? hotKey.cmdMap : hotKey.layerMap[layer]; if (layers) { TclObject result; for (auto& p : hotKey.layerMap) { // An alternative for this test is to always properly // prune layerMap. ATM this approach seems simpler. if (!p.second.empty()) { result.addListElement(p.first); } } return result.getString().str(); } string result; switch (tokens.size()) { case 0: UNREACHABLE; case 1: // show all bounded keys (for this layer) for (auto& p : cmdMap) { result += formatBinding(p); } break; case 2: { // show bindings for this key (in this layer) auto it = cmdMap.find(createEvent(tokens[1])); if (it == cmdMap.end()) { throw CommandException("Key not bound"); } result = formatBinding(*it); break; } default: { // make a new binding string command; bool repeat = false; unsigned start = 2; if (tokens[2] == "-repeat") { repeat = true; ++start; } for (unsigned i = start; i < tokens.size(); ++i) { if (i != start) command += ' '; command += tokens[i]; } HotKey::HotKeyInfo info(command, repeat); auto event = createEvent(tokens[1]); if (defaultCmd) { hotKey.bindDefault(event, info); } else if (layer.empty()) { hotKey.bind(event, info); } else { hotKey.bindLayer(event, info, layer); } break; } } return result; } string BindCmd::help(const vector& /*tokens*/) const { string cmd = getBindCmdName(defaultCmd); return cmd + " : show all bounded keys\n" + cmd + " : show binding for this key\n" + cmd + " [-repeat] : bind key to command, optionally " "repeat command while key remains pressed\n" "These 3 take an optional '-layer ' option, " "see activate_input_layer." + cmd + " -layers : show a list of layers with bound keys\n"; } // class UnbindCmd static string getUnbindCmdName(bool defaultCmd) { return defaultCmd ? "unbind_default" : "unbind"; } UnbindCmd::UnbindCmd(CommandController& commandController, HotKey& hotKey_, bool defaultCmd_) : Command(commandController, getUnbindCmdName(defaultCmd_)) , hotKey(hotKey_) , defaultCmd(defaultCmd_) { } string UnbindCmd::execute(const vector& tokens_) { string layer; bool layers; auto tokens = parse(defaultCmd, tokens_, layer, layers); if (layers) { throw SyntaxError(); } if ((tokens.size() > 2) || (layer.empty() && (tokens.size() != 2))) { throw SyntaxError(); } HotKey::EventPtr event; if (tokens.size() == 2) { event = createEvent(tokens[1]); } if (defaultCmd) { assert(event); hotKey.unbindDefault(event); } else if (layer.empty()) { assert(event); hotKey.unbind(event); } else { if (event) { hotKey.unbindLayer(event, layer); } else { hotKey.unbindFullLayer(layer); } } return ""; } string UnbindCmd::help(const vector& /*tokens*/) const { string cmd = getUnbindCmdName(defaultCmd); return cmd + " : unbind this key\n" + cmd + " -layer : unbind key in a specific layer\n" + cmd + " -layer : unbind all keys in this layer\n"; } // class ActivateCmd ActivateCmd::ActivateCmd(CommandController& commandController, HotKey& hotKey_) : Command(commandController, "activate_input_layer") , hotKey(hotKey_) { } string ActivateCmd::execute(const vector& tokens) { string layer; bool blocking = false; for (size_t i = 1; i < tokens.size(); ++i) { if (tokens[i] == "-blocking") { blocking = true; } else { if (!layer.empty()) { throw SyntaxError(); } layer = tokens[i]; } } string result; if (layer.empty()) { for (auto it = hotKey.activeLayers.rbegin(); it != hotKey.activeLayers.rend(); ++it) { result += it->layer; if (it->blocking) { result += " -blocking"; } result += '\n'; } } else { hotKey.activateLayer(layer, blocking); } return result; } string ActivateCmd::help(const vector& /*tokens*/) const { return "activate_input_layer " ": show list of active layers (most recent on top)\n" "activate_input_layer [-blocking] " ": activate new layer, optionally in blocking mode\n"; } // class DeactivateCmd DeactivateCmd::DeactivateCmd(CommandController& commandController, HotKey& hotKey_) : Command(commandController, "deactivate_input_layer") , hotKey(hotKey_) { } string DeactivateCmd::execute(const vector& tokens) { if (tokens.size() != 2) { throw SyntaxError(); } hotKey.deactivateLayer(tokens[1]); return ""; } string DeactivateCmd::help(const vector& /*tokens*/) const { return "deactivate_input_layer : deactive the given input layer"; } } // namespace openmsx openmsx-0.10.0/src/events/InputEvents.cc0000644000175000017500000003065012262345041020736 0ustar manuelmanuel00000000000000#include "InputEvents.hh" #include "Keys.hh" #include "TclObject.hh" #include "StringOp.hh" #include "Timer.hh" #include "checked_cast.hh" #include "openmsx.hh" #include #include #include using std::make_tuple; using std::string; using std::vector; namespace openmsx { // class TimedEvent TimedEvent::TimedEvent(EventType type) : Event(type) , realtime(Timer::getTime()) { } // class KeyEvent #if PLATFORM_ANDROID // The unicode support in the SDL Android port is currently broken. // It always sets the unicode value equal to the keycode value, even for non-character // keys like the function keys. Furthermore, it has the unicode value set on both // press and release, while SDL on other platforms only sets unicode value on press. // As a workaround, set unicode to 0 for non-character keys and on release for any key, // until SDL Android port has been fixed. // Furthermore, try to set unicode value to correct character, taking into consideration // the modifier keys. The assumption is that Android has a qwerty keyboard, which is // true for the standard virtual keyboard of android 4.0, 4.1 and 4.2 and also true // for the more convenient "hackers keyboard" app. // However, some Android devices with a physical keyboard might have an Azerty keyboard // I don't know what the SDL layer does with the key events received from such Azerty // keyboard. Probably it won't work well with this work-around code. Must eventually fix // the unicode support in the SDL Android port, together with the main developer of the port. static uint16_t fixUnicode(Keys::KeyCode keyCode, uint16_t brokenUnicode) { Keys::KeyCode maskedKeyCode = (Keys::KeyCode)(int(brokenUnicode) & int(Keys::K_MASK)); if (brokenUnicode & Keys::KD_RELEASE) { return 0; } if (maskedKeyCode >= Keys::K_UP) { return 0; } if (maskedKeyCode >= Keys::K_WORLD_90 && maskedKeyCode <= Keys::K_WORLD_95) { return 0; } if ((keyCode & Keys::KM_SHIFT) == Keys::KM_SHIFT) { if (maskedKeyCode >= Keys::K_A && maskedKeyCode <= Keys::K_Z) { // Convert lowercase character into uppercase return brokenUnicode - 32; } // Convert several characters, assuming user has a qwerty keyboard on the Android or that Android has translated everything // to qwerty keyboard combinations before passing the events to the SDL layer. // Note that the 'rows' mentioned in below mapping table are based on the "hackers keyboard" app. Though // this mapping turns out to work fine with the standard Android 4.x keyboard app as well. switch (maskedKeyCode) { // row 1 case Keys::K_1: return uint16_t('!'); case Keys::K_2: return uint16_t('@'); case Keys::K_3: return uint16_t('#'); case Keys::K_4: return uint16_t('$'); case Keys::K_5: return uint16_t('%'); case Keys::K_6: return uint16_t('^'); case Keys::K_7: return uint16_t('&'); case Keys::K_8: return uint16_t('*'); case Keys::K_9: return uint16_t('('); case Keys::K_0: return uint16_t(')'); case Keys::K_MINUS: return uint16_t('_'); case Keys::K_EQUALS: return uint16_t('+'); // row 2 case Keys::K_LEFTBRACKET: return uint16_t('{'); case Keys::K_RIGHTBRACKET: return uint16_t('}'); case Keys::K_BACKSLASH: return uint16_t('|'); // row 3 case Keys::K_SEMICOLON: return uint16_t(':'); case Keys::K_QUOTE: return uint16_t('"'); // row 4 case Keys::K_COMMA: return uint16_t('<'); case Keys::K_PERIOD: return uint16_t('>'); case Keys::K_SLASH: return uint16_t('?'); } } return brokenUnicode; } KeyEvent::KeyEvent(EventType type, Keys::KeyCode keyCode_, uint16_t unicode_) : TimedEvent(type), keyCode(keyCode_), unicode(fixUnicode(keyCode_, unicode_)) { } #else KeyEvent::KeyEvent(EventType type, Keys::KeyCode keyCode_, uint16_t unicode_) : TimedEvent(type), keyCode(keyCode_), unicode(unicode_) { } #endif void KeyEvent::toStringImpl(TclObject& result) const { result.addListElement("keyb"); result.addListElement(Keys::getName(getKeyCode())); if (getUnicode() != 0) { result.addListElement(StringOp::Builder() << "unicode" << getUnicode()); } } bool KeyEvent::lessImpl(const Event& other) const { // note: don't compare unicode auto& o = checked_cast(other); return getKeyCode() < o.getKeyCode(); } // class KeyUpEvent KeyUpEvent::KeyUpEvent(Keys::KeyCode keyCode) : KeyEvent(OPENMSX_KEY_UP_EVENT, keyCode, uint16_t(0)) { } KeyUpEvent::KeyUpEvent(Keys::KeyCode keyCode, uint16_t unicode) : KeyEvent(OPENMSX_KEY_UP_EVENT, keyCode, unicode) { } // class KeyDownEvent KeyDownEvent::KeyDownEvent(Keys::KeyCode keyCode) : KeyEvent(OPENMSX_KEY_DOWN_EVENT, keyCode, uint16_t(0)) { } KeyDownEvent::KeyDownEvent(Keys::KeyCode keyCode, uint16_t unicode) : KeyEvent(OPENMSX_KEY_DOWN_EVENT, keyCode, unicode) { } // class MouseButtonEvent MouseButtonEvent::MouseButtonEvent(EventType type, unsigned button_) : TimedEvent(type), button(button_) { } void MouseButtonEvent::toStringHelper(TclObject& result) const { result.addListElement("mouse"); result.addListElement(StringOp::Builder() << "button" << getButton()); } bool MouseButtonEvent::lessImpl(const Event& other) const { auto& o = checked_cast(other); return getButton() < o.getButton(); } // class MouseButtonUpEvent MouseButtonUpEvent::MouseButtonUpEvent(unsigned button) : MouseButtonEvent(OPENMSX_MOUSE_BUTTON_UP_EVENT, button) { } void MouseButtonUpEvent::toStringImpl(TclObject& result) const { toStringHelper(result); result.addListElement("up"); } // class MouseButtonDownEvent MouseButtonDownEvent::MouseButtonDownEvent(unsigned button) : MouseButtonEvent(OPENMSX_MOUSE_BUTTON_DOWN_EVENT, button) { } void MouseButtonDownEvent::toStringImpl(TclObject& result) const { toStringHelper(result); result.addListElement("down"); } // class MouseMotionEvent MouseMotionEvent::MouseMotionEvent(int xrel_, int yrel_, int xabs_, int yabs_) : TimedEvent(OPENMSX_MOUSE_MOTION_EVENT) , xrel(xrel_), yrel(yrel_) , xabs(xabs_), yabs(yabs_) { } void MouseMotionEvent::toStringImpl(TclObject& result) const { result.addListElement("mouse"); result.addListElement("motion"); result.addListElement(getX()); result.addListElement(getY()); result.addListElement(getAbsX()); result.addListElement(getAbsY()); } bool MouseMotionEvent::lessImpl(const Event& other) const { auto& o = checked_cast(other); return make_tuple( getX(), getY(), getAbsX(), getAbsY()) < make_tuple(o.getX(), o.getY(), o.getAbsX(), o.getAbsY()); } // class MouseMotionGroupEvent MouseMotionGroupEvent::MouseMotionGroupEvent() : Event(OPENMSX_MOUSE_MOTION_GROUP_EVENT) { } void MouseMotionGroupEvent::toStringImpl(TclObject& result) const { result.addListElement("mouse"); result.addListElement("motion"); } bool MouseMotionGroupEvent::lessImpl(const Event& /*other*/) const { // All MouseMotionGroup events are equivalent return false; } bool MouseMotionGroupEvent::matches(const Event& other) const { return other.getType() == OPENMSX_MOUSE_MOTION_EVENT; } // class JoystickEvent JoystickEvent::JoystickEvent(EventType type, unsigned joystick_) : TimedEvent(type), joystick(joystick_) { } void JoystickEvent::toStringHelper(TclObject& result) const { result.addListElement(StringOp::Builder() << "joy" << getJoystick() + 1); } bool JoystickEvent::lessImpl(const Event& other) const { auto& o = checked_cast(other); return (getJoystick() != o.getJoystick()) ? (getJoystick() < o.getJoystick()) : lessImpl(o); } // class JoystickButtonEvent JoystickButtonEvent::JoystickButtonEvent( EventType type, unsigned joystick, unsigned button_) : JoystickEvent(type, joystick), button(button_) { } void JoystickButtonEvent::toStringHelper(TclObject& result) const { JoystickEvent::toStringHelper(result); result.addListElement(StringOp::Builder() << "button" << getButton()); } bool JoystickButtonEvent::lessImpl(const JoystickEvent& other) const { auto& o = checked_cast(other); return getButton() < o.getButton(); } // class JoystickButtonUpEvent JoystickButtonUpEvent::JoystickButtonUpEvent(unsigned joystick, unsigned button) : JoystickButtonEvent(OPENMSX_JOY_BUTTON_UP_EVENT, joystick, button) { } void JoystickButtonUpEvent::toStringImpl(TclObject& result) const { toStringHelper(result); result.addListElement("up"); } // class JoystickButtonDownEvent JoystickButtonDownEvent::JoystickButtonDownEvent(unsigned joystick, unsigned button) : JoystickButtonEvent(OPENMSX_JOY_BUTTON_DOWN_EVENT, joystick, button) { } void JoystickButtonDownEvent::toStringImpl(TclObject& result) const { toStringHelper(result); result.addListElement("down"); } // class JoystickAxisMotionEvent JoystickAxisMotionEvent::JoystickAxisMotionEvent( unsigned joystick, unsigned axis_, short value_) : JoystickEvent(OPENMSX_JOY_AXIS_MOTION_EVENT, joystick) , axis(axis_), value(value_) { } void JoystickAxisMotionEvent::toStringImpl(TclObject& result) const { toStringHelper(result); result.addListElement(StringOp::Builder() << "axis" << getAxis()); result.addListElement(getValue()); } bool JoystickAxisMotionEvent::lessImpl(const JoystickEvent& other) const { auto& o = checked_cast(other); return make_tuple( getAxis(), getValue()) < make_tuple(o.getAxis(), o.getValue()); } // class FocusEvent FocusEvent::FocusEvent(bool gain_) : Event(OPENMSX_FOCUS_EVENT), gain(gain_) { } void FocusEvent::toStringImpl(TclObject& result) const { result.addListElement("focus"); result.addListElement(getGain()); } bool FocusEvent::lessImpl(const Event& other) const { auto& o = checked_cast(other); return getGain() < o.getGain(); } // class ResizeEvent ResizeEvent::ResizeEvent(unsigned x_, unsigned y_) : Event(OPENMSX_RESIZE_EVENT), x(x_), y(y_) { } void ResizeEvent::toStringImpl(TclObject& result) const { result.addListElement("resize"); result.addListElement(int(getX())); result.addListElement(int(getY())); } bool ResizeEvent::lessImpl(const Event& other) const { auto& o = checked_cast(other); return make_tuple( getX(), getY()) < make_tuple(o.getX(), o.getY()); } // class QuitEvent QuitEvent::QuitEvent() : Event(OPENMSX_QUIT_EVENT) { } void QuitEvent::toStringImpl(TclObject& result) const { result.addListElement("quit"); } bool QuitEvent::lessImpl(const Event& /*other*/) const { return false; } // class OsdControlEvent OsdControlEvent::OsdControlEvent( EventType type, unsigned button_, const std::shared_ptr& origEvent_) : TimedEvent(type), origEvent(origEvent_), button(button_) { } bool OsdControlEvent::isRepeatStopper(const Event& other) const { // If this OsdControlEvent was geneated by the other event, then // repeat should not be stopped. if (origEvent.get() == &other) return false; // If this OsdControlEvent event was generated by a joystick motion // event and the new event is also a joystick motion event then don't // stop repeat. We don't need to check the actual values of the events // (it also isn't trivial), because when the values differ by enough, // a new OsdControlEvent will be generated and that one will stop // repeat. return !dynamic_cast(origEvent.get()) || !dynamic_cast(&other); } void OsdControlEvent::toStringHelper(TclObject& result) const { result.addListElement("OSDcontrol"); static const char* const names[] = { "LEFT", "RIGHT", "UP", "DOWN", "A", "B" }; result.addListElement(names[getButton()]); } bool OsdControlEvent::lessImpl(const Event& other) const { auto& o = checked_cast(other); return getButton() < o.getButton(); } // class OsdControlReleaseEvent OsdControlReleaseEvent::OsdControlReleaseEvent( unsigned button, const std::shared_ptr& origEvent) : OsdControlEvent(OPENMSX_OSD_CONTROL_RELEASE_EVENT, button, origEvent) { } void OsdControlReleaseEvent::toStringImpl(TclObject& result) const { toStringHelper(result); result.addListElement("RELEASE"); } // class OsdControlPressEvent OsdControlPressEvent::OsdControlPressEvent( unsigned button, const std::shared_ptr& origEvent) : OsdControlEvent(OPENMSX_OSD_CONTROL_PRESS_EVENT, button, origEvent) { } void OsdControlPressEvent::toStringImpl(TclObject& result) const { toStringHelper(result); result.addListElement("PRESS"); } } // namespace openmsx openmsx-0.10.0/src/events/InputEventGenerator.cc0000644000175000017500000003225012262345041022420 0ustar manuelmanuel00000000000000#include "Command.hh" #include "InputEventGenerator.hh" #include "EventDistributor.hh" #include "InputEvents.hh" #include "BooleanSetting.hh" #include "Keys.hh" #include "openmsx.hh" #include "checked_cast.hh" #include "memory.hh" #include "unreachable.hh" #include "build-info.hh" #include using std::string; using std::vector; using std::make_shared; namespace openmsx { class EscapeGrabCmd : public Command { public: EscapeGrabCmd(CommandController& commandController, InputEventGenerator& inputEventGenerator); virtual string execute(const vector& tokens); virtual string help(const vector& tokens) const; private: InputEventGenerator& inputEventGenerator; }; bool InputEventGenerator::androidButtonA = false; bool InputEventGenerator::androidButtonB = false; InputEventGenerator::InputEventGenerator(CommandController& commandController, EventDistributor& eventDistributor_) : eventDistributor(eventDistributor_) , grabInput(make_unique( commandController, "grabinput", "This setting controls if openMSX takes over mouse and keyboard input", false, Setting::DONT_SAVE)) , escapeGrabCmd(make_unique(commandController, *this)) , escapeGrabState(ESCAPE_GRAB_WAIT_CMD) , keyRepeat(false) { setGrabInput(grabInput->getBoolean()); grabInput->attach(*this); eventDistributor.registerEventListener(OPENMSX_FOCUS_EVENT, *this); eventDistributor.registerEventListener(OPENMSX_POLL_EVENT, *this); reinit(); osdControlButtonsState = unsigned(~0); // 0 is pressed, 1 is released } InputEventGenerator::~InputEventGenerator() { eventDistributor.unregisterEventListener(OPENMSX_POLL_EVENT, *this); eventDistributor.unregisterEventListener(OPENMSX_FOCUS_EVENT, *this); grabInput->detach(*this); } void InputEventGenerator::reinit() { SDL_EnableUNICODE(1); setKeyRepeat(keyRepeat); } void InputEventGenerator::wait() { // SDL bug workaround if (!SDL_WasInit(SDL_INIT_VIDEO)) { SDL_Delay(100); } if (SDL_WaitEvent(nullptr)) { poll(); } } void InputEventGenerator::poll() { SDL_Event event; while (SDL_PollEvent(&event) == 1) { #ifdef DEBUG string t; switch (event.type) { case SDL_ACTIVEEVENT: t = "SDL_ACTIVEEVENT"; break; case SDL_KEYDOWN: t = "SDL_KEYDOWN"; break; case SDL_KEYUP: t = "SDL_KEYUP"; break; case SDL_MOUSEMOTION: t = "SDL_MOUSEMOTION"; break; case SDL_MOUSEBUTTONDOWN: t = "SDL_MOUSEBUTTONDOWN"; break; case SDL_MOUSEBUTTONUP: t = "SDL_MOUSEBUTTONUP"; break; case SDL_JOYAXISMOTION: t = "SDL_JOYAXISMOTION"; break; case SDL_JOYBALLMOTION: t = "SDL_JOYBALLMOTION"; break; case SDL_JOYHATMOTION: t = "SDL_JOYHATMOTION"; break; case SDL_JOYBUTTONDOWN: t = "SDL_JOYBUTTONDOWN"; break; case SDL_JOYBUTTONUP: t = "SDL_JOYBUTTONUP"; break; case SDL_QUIT: t = "SDL_QUIT"; break; case SDL_SYSWMEVENT: t = "SDL_SYSWMEVENT"; break; case SDL_VIDEORESIZE: t = "SDL_VIDEORESIZE"; break; case SDL_VIDEOEXPOSE: t = "SDL_VIDEOEXPOSE"; break; case SDL_USEREVENT: t = "SDL_USEREVENT"; break; default: t = "UNKNOWN"; break; } PRT_DEBUG("SDL event received, type: " + t); #endif handle(event); } } void InputEventGenerator::setKeyRepeat(bool enable) { keyRepeat = enable; if (keyRepeat) { SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); } else { SDL_EnableKeyRepeat(0, 0); } } #if PLATFORM_ANDROID //TODO: make JOYVALUE_THRESHOLD dynamic, depending on virtual key size static const int JOYVALUE_THRESHOLD = 4 * 32768 / 10; #else static const int JOYVALUE_THRESHOLD = 32768 / 10; #endif void InputEventGenerator::setNewOsdControlButtonState( unsigned newState, const EventPtr& origEvent) { unsigned deltaState = osdControlButtonsState ^ newState; for (unsigned i = OsdControlEvent::LEFT_BUTTON; i <= OsdControlEvent::B_BUTTON; ++i) { if (deltaState & (1 << i)) { if (newState & (1 << i)) { eventDistributor.distributeEvent( make_shared( i, origEvent)); } else { eventDistributor.distributeEvent( make_shared( i, origEvent)); } } } osdControlButtonsState = newState; } void InputEventGenerator::triggerOsdControlEventsFromJoystickAxisMotion( unsigned axis, short value, const EventPtr& origEvent) { short normalized_value = (value < -JOYVALUE_THRESHOLD ? -1 : (value > JOYVALUE_THRESHOLD ? 1 : 0)); unsigned neg_button, pos_button; switch (axis) { case 0: neg_button = 1 << OsdControlEvent::LEFT_BUTTON; pos_button = 1 << OsdControlEvent::RIGHT_BUTTON; break; // axis 0 case 1: neg_button = 1 << OsdControlEvent::UP_BUTTON; pos_button = 1 << OsdControlEvent::DOWN_BUTTON; break; default: // Ignore all other axis (3D joysticks and flight joysticks may // have more than 2 axis) return; } switch (normalized_value) { case 0: // release both buttons setNewOsdControlButtonState( osdControlButtonsState | neg_button | pos_button, origEvent); break; case 1: // release negative button, press positive button setNewOsdControlButtonState( (osdControlButtonsState | neg_button) & ~pos_button, origEvent); break; case -1: // press negative button, release positive button setNewOsdControlButtonState( (osdControlButtonsState | pos_button) & ~neg_button, origEvent); break; } } void InputEventGenerator::osdControlChangeButton( bool up, unsigned changedButtonMask, const EventPtr& origEvent) { auto newButtonState = up ? osdControlButtonsState | changedButtonMask : osdControlButtonsState & ~changedButtonMask; setNewOsdControlButtonState(newButtonState, origEvent); } void InputEventGenerator::triggerOsdControlEventsFromJoystickButtonEvent( unsigned button, bool up, const EventPtr& origEvent) { osdControlChangeButton( up, ((button & 1) ? (1 << OsdControlEvent::B_BUTTON) : (1 << OsdControlEvent::A_BUTTON)), origEvent); } void InputEventGenerator::triggerOsdControlEventsFromKeyEvent( Keys::KeyCode keyCode, bool up, const EventPtr& origEvent) { keyCode = static_cast(keyCode & Keys::K_MASK); if (keyCode == Keys::K_LEFT) { osdControlChangeButton(up, 1 << OsdControlEvent::LEFT_BUTTON, origEvent); } else if (keyCode == Keys::K_RIGHT) { osdControlChangeButton(up, 1 << OsdControlEvent::RIGHT_BUTTON, origEvent); } else if (keyCode == Keys::K_UP) { osdControlChangeButton(up, 1 << OsdControlEvent::UP_BUTTON, origEvent); } else if (keyCode == Keys::K_DOWN) { osdControlChangeButton(up, 1 << OsdControlEvent::DOWN_BUTTON, origEvent); } else if (keyCode == Keys::K_SPACE || keyCode == Keys::K_RETURN) { osdControlChangeButton(up, 1 << OsdControlEvent::A_BUTTON, origEvent); } else if (keyCode == Keys::K_ESCAPE) { osdControlChangeButton(up, 1 << OsdControlEvent::B_BUTTON, origEvent); } } void InputEventGenerator::handle(const SDL_Event& evt) { EventPtr event; switch (evt.type) { case SDL_KEYUP: // Virtual joystick of SDL Android port does not have joystick // buttons. It has however up to 6 virtual buttons that can be // mapped to SDL keyboard events. Two of these virtual buttons // will be mapped to keys SDLK_WORLD_93 and 94 and are // interpeted here as joystick buttons (respectively button 0 // and 1). if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_93) { event = make_shared(0, 0); triggerOsdControlEventsFromJoystickButtonEvent( 0, true, event); androidButtonA = false; } else if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_94) { event = make_shared(0, 1); triggerOsdControlEventsFromJoystickButtonEvent( 1, true, event); androidButtonB = false; } else { auto keyCode = Keys::getCode( evt.key.keysym.sym, evt.key.keysym.mod, evt.key.keysym.scancode, true); event = make_shared( keyCode, evt.key.keysym.unicode); triggerOsdControlEventsFromKeyEvent(keyCode, true, event); } break; case SDL_KEYDOWN: if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_93) { event = make_shared(0, 0); triggerOsdControlEventsFromJoystickButtonEvent( 0, false, event); androidButtonA = true; } else if (PLATFORM_ANDROID && evt.key.keysym.sym == SDLK_WORLD_94) { event = make_shared(0, 1); triggerOsdControlEventsFromJoystickButtonEvent( 1, false, event); androidButtonB = true; } else { auto keyCode = Keys::getCode( evt.key.keysym.sym, evt.key.keysym.mod, evt.key.keysym.scancode, false); event = make_shared( keyCode, evt.key.keysym.unicode); triggerOsdControlEventsFromKeyEvent(keyCode, false, event); } break; case SDL_MOUSEBUTTONUP: event = make_shared(evt.button.button); break; case SDL_MOUSEBUTTONDOWN: event = make_shared(evt.button.button); break; case SDL_MOUSEMOTION: event = make_shared( evt.motion.xrel, evt.motion.yrel, evt.motion.x, evt.motion.y); break; case SDL_JOYBUTTONUP: event = make_shared( evt.jbutton.which, evt.jbutton.button); triggerOsdControlEventsFromJoystickButtonEvent( evt.jbutton.button, true, event); break; case SDL_JOYBUTTONDOWN: event = make_shared( evt.jbutton.which, evt.jbutton.button); triggerOsdControlEventsFromJoystickButtonEvent( evt.jbutton.button, false, event); break; case SDL_JOYAXISMOTION: event = make_shared( evt.jaxis.which, evt.jaxis.axis, evt.jaxis.value); triggerOsdControlEventsFromJoystickAxisMotion( evt.jaxis.axis, evt.jaxis.value, event); break; case SDL_ACTIVEEVENT: event = make_shared(evt.active.gain != 0); break; case SDL_VIDEORESIZE: event = make_shared(evt.resize.w, evt.resize.h); break; case SDL_VIDEOEXPOSE: event = make_shared(OPENMSX_EXPOSE_EVENT); break; case SDL_QUIT: event = make_shared(); break; default: break; } #ifdef DEBUG if (event) { PRT_DEBUG("SDL event converted to: " + event->toString()); } else { PRT_DEBUG("SDL event was of unknown type, not converted to an openMSX event"); } #endif if (event) eventDistributor.distributeEvent(event); } void InputEventGenerator::update(const Setting& setting) { (void)setting; assert(&setting == grabInput.get()); escapeGrabState = ESCAPE_GRAB_WAIT_CMD; setGrabInput(grabInput->getBoolean()); } int InputEventGenerator::signalEvent(const std::shared_ptr& event) { if (event->getType() == OPENMSX_FOCUS_EVENT) { auto& focusEvent = checked_cast(*event); switch (escapeGrabState) { case ESCAPE_GRAB_WAIT_CMD: // nothing break; case ESCAPE_GRAB_WAIT_LOST: if (focusEvent.getGain() == false) { escapeGrabState = ESCAPE_GRAB_WAIT_GAIN; } break; case ESCAPE_GRAB_WAIT_GAIN: if (focusEvent.getGain() == true) { escapeGrabState = ESCAPE_GRAB_WAIT_CMD; } setGrabInput(true); break; default: UNREACHABLE; } } else if (event->getType() == OPENMSX_POLL_EVENT) { poll(); } return 0; } void InputEventGenerator::setGrabInput(bool grab) { // Note that this setting is also changed in VisibleSurface constructor // because for Mac we want to enable it in fullscreen. // It's not worth it to get that exactly right here, because here // we don't have easy access to renderer settings and it may only // go wrong if you explicitly change grab input at full screen (on Mac) SDL_WM_GrabInput(grab ? SDL_GRAB_ON : SDL_GRAB_OFF); } // Wrap SDL joystick button functions to handle the 'fake' android joystick // buttons. The method InputEventGenerator::handle() already takes care of fake // events for the andoid joystick buttons, these two wrappers handle the direct // joystick button state queries. int InputEventGenerator::joystickNumButtons(SDL_Joystick* joystick) { if (PLATFORM_ANDROID) { return 2; } else { return SDL_JoystickNumButtons(joystick); } } bool InputEventGenerator::joystickGetButton(SDL_Joystick* joystick, int button) { if (PLATFORM_ANDROID) { switch (button) { case 0: return androidButtonA; case 1: return androidButtonB; default: UNREACHABLE; return false; } } else { return SDL_JoystickGetButton(joystick, button) != 0; } } // class EscapeGrabCmd EscapeGrabCmd::EscapeGrabCmd(CommandController& commandController, InputEventGenerator& inputEventGenerator_) : Command(commandController, "escape_grab") , inputEventGenerator(inputEventGenerator_) { } string EscapeGrabCmd::execute(const vector& /*tokens*/) { if (inputEventGenerator.grabInput->getBoolean()) { inputEventGenerator.escapeGrabState = InputEventGenerator::ESCAPE_GRAB_WAIT_LOST; inputEventGenerator.setGrabInput(false); } return ""; } string EscapeGrabCmd::help(const vector& /*tokens*/) const { return "Temporarily release input grab."; } } // namespace openmsx openmsx-0.10.0/src/events/CliListener.hh0000644000175000017500000000063112262345041020675 0ustar manuelmanuel00000000000000#ifndef CLILISTENER_HH #define CLILISTENER_HH #include "CliComm.hh" namespace openmsx { class CliListener { public: virtual ~CliListener() {} virtual void log(CliComm::LogLevel level, string_ref message) = 0; virtual void update(CliComm::UpdateType type, string_ref machine, string_ref name, string_ref value) = 0; protected: CliListener() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/events/TclCallbackMessages.hh0000644000175000017500000000124312262345041022307 0ustar manuelmanuel00000000000000#ifndef TCLCALLBACKMESSAGES_HH #define TCLCALLBACKMESSAGES_HH #include "CliListener.hh" #include namespace openmsx { class GlobalCliComm; class CommandController; class TclCallback; class TclCallbackMessages : public CliListener { public: TclCallbackMessages(GlobalCliComm& cliComm, CommandController& controller); virtual ~TclCallbackMessages(); virtual void log(CliComm::LogLevel level, string_ref message); virtual void update(CliComm::UpdateType type, string_ref machine, string_ref name, string_ref value); private: GlobalCliComm& cliComm; const std::unique_ptr messageCallback; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/Keys.hh0000644000175000017500000002016612262345041017400 0ustar manuelmanuel00000000000000#ifndef KEYS_HH #define KEYS_HH #include "string_ref.hh" #include #include // TODO #include namespace openmsx { namespace Keys { /** * Constants that identify keys and key modifiers. * * There are two special key codes: * - K_NONE : returned when we do string -> key code * translation and there is no key with given * name. Most likely the keyname was misspelled. * - K_UNKNOWN : this code is returned when a real key was * pressed, but we have no idea which key it is. * Should only happen when the user has some * exotic keyboard. Note that it might be possible * that there are multiple keys that produce this * code. */ enum KeyCode { K_NONE = -1, K_UNKNOWN = SDLK_UNKNOWN, K_BACKSPACE = SDLK_BACKSPACE, K_TAB = SDLK_TAB, K_CLEAR = SDLK_CLEAR, K_RETURN = SDLK_RETURN, K_PAUSE = SDLK_PAUSE, K_ESCAPE = SDLK_ESCAPE, K_SPACE = SDLK_SPACE, K_EXCLAIM = SDLK_EXCLAIM, K_QUOTEDBL = SDLK_QUOTEDBL, K_HASH = SDLK_HASH, K_DOLLAR = SDLK_DOLLAR, K_AMPERSAND = SDLK_AMPERSAND, K_QUOTE = SDLK_QUOTE, K_LEFTPAREN = SDLK_LEFTPAREN, K_RIGHTPAREN = SDLK_RIGHTPAREN, K_ASTERISK = SDLK_ASTERISK, K_PLUS = SDLK_PLUS, K_COMMA = SDLK_COMMA, K_MINUS = SDLK_MINUS, K_PERIOD = SDLK_PERIOD, K_SLASH = SDLK_SLASH, K_0 = SDLK_0, K_1 = SDLK_1, K_2 = SDLK_2, K_3 = SDLK_3, K_4 = SDLK_4, K_5 = SDLK_5, K_6 = SDLK_6, K_7 = SDLK_7, K_8 = SDLK_8, K_9 = SDLK_9, K_COLON = SDLK_COLON, K_SEMICOLON = SDLK_SEMICOLON, K_LESS = SDLK_LESS, K_EQUALS = SDLK_EQUALS, K_GREATER = SDLK_GREATER, K_QUESTION = SDLK_QUESTION, K_AT = SDLK_AT, K_LEFTBRACKET = SDLK_LEFTBRACKET, K_BACKSLASH = SDLK_BACKSLASH, K_RIGHTBRACKET = SDLK_RIGHTBRACKET, K_CARET = SDLK_CARET, K_UNDERSCORE = SDLK_UNDERSCORE, K_BACKQUOTE = SDLK_BACKQUOTE, K_A = SDLK_a, K_B = SDLK_b, K_C = SDLK_c, K_D = SDLK_d, K_E = SDLK_e, K_F = SDLK_f, K_G = SDLK_g, K_H = SDLK_h, K_I = SDLK_i, K_J = SDLK_j, K_K = SDLK_k, K_L = SDLK_l, K_M = SDLK_m, K_N = SDLK_n, K_O = SDLK_o, K_P = SDLK_p, K_Q = SDLK_q, K_R = SDLK_r, K_S = SDLK_s, K_T = SDLK_t, K_U = SDLK_u, K_V = SDLK_v, K_W = SDLK_w, K_X = SDLK_x, K_Y = SDLK_y, K_Z = SDLK_z, K_DELETE = SDLK_DELETE, K_WORLD_0 = SDLK_WORLD_0, K_WORLD_1 = SDLK_WORLD_1, K_WORLD_2 = SDLK_WORLD_2, K_WORLD_3 = SDLK_WORLD_3, K_WORLD_4 = SDLK_WORLD_4, K_WORLD_5 = SDLK_WORLD_5, K_WORLD_6 = SDLK_WORLD_6, K_WORLD_7 = SDLK_WORLD_7, K_WORLD_8 = SDLK_WORLD_8, K_WORLD_9 = SDLK_WORLD_9, K_WORLD_10 = SDLK_WORLD_10, K_WORLD_11 = SDLK_WORLD_11, K_WORLD_12 = SDLK_WORLD_12, K_WORLD_13 = SDLK_WORLD_13, K_WORLD_14 = SDLK_WORLD_14, K_WORLD_15 = SDLK_WORLD_15, K_WORLD_16 = SDLK_WORLD_16, K_WORLD_17 = SDLK_WORLD_17, K_WORLD_18 = SDLK_WORLD_18, K_WORLD_19 = SDLK_WORLD_19, K_WORLD_20 = SDLK_WORLD_20, K_WORLD_21 = SDLK_WORLD_21, K_WORLD_22 = SDLK_WORLD_22, K_WORLD_23 = SDLK_WORLD_23, K_WORLD_24 = SDLK_WORLD_24, K_WORLD_25 = SDLK_WORLD_25, K_WORLD_26 = SDLK_WORLD_26, K_WORLD_27 = SDLK_WORLD_27, K_WORLD_28 = SDLK_WORLD_28, K_WORLD_29 = SDLK_WORLD_29, K_WORLD_30 = SDLK_WORLD_30, K_WORLD_31 = SDLK_WORLD_31, K_WORLD_32 = SDLK_WORLD_32, K_WORLD_33 = SDLK_WORLD_33, K_WORLD_34 = SDLK_WORLD_34, K_WORLD_35 = SDLK_WORLD_35, K_WORLD_36 = SDLK_WORLD_36, K_WORLD_37 = SDLK_WORLD_37, K_WORLD_38 = SDLK_WORLD_38, K_WORLD_39 = SDLK_WORLD_39, K_WORLD_40 = SDLK_WORLD_40, K_WORLD_41 = SDLK_WORLD_41, K_WORLD_42 = SDLK_WORLD_42, K_WORLD_43 = SDLK_WORLD_43, K_WORLD_44 = SDLK_WORLD_44, K_WORLD_45 = SDLK_WORLD_45, K_WORLD_46 = SDLK_WORLD_46, K_WORLD_47 = SDLK_WORLD_47, K_WORLD_48 = SDLK_WORLD_48, K_WORLD_49 = SDLK_WORLD_49, K_WORLD_50 = SDLK_WORLD_50, K_WORLD_51 = SDLK_WORLD_51, K_WORLD_52 = SDLK_WORLD_52, K_WORLD_53 = SDLK_WORLD_53, K_WORLD_54 = SDLK_WORLD_54, K_WORLD_55 = SDLK_WORLD_55, K_WORLD_56 = SDLK_WORLD_56, K_WORLD_57 = SDLK_WORLD_57, K_WORLD_58 = SDLK_WORLD_58, K_WORLD_59 = SDLK_WORLD_59, K_WORLD_60 = SDLK_WORLD_60, K_WORLD_61 = SDLK_WORLD_61, K_WORLD_62 = SDLK_WORLD_62, K_WORLD_63 = SDLK_WORLD_63, K_WORLD_64 = SDLK_WORLD_64, K_WORLD_65 = SDLK_WORLD_65, K_WORLD_66 = SDLK_WORLD_66, K_WORLD_67 = SDLK_WORLD_67, K_WORLD_68 = SDLK_WORLD_68, K_WORLD_69 = SDLK_WORLD_69, K_WORLD_70 = SDLK_WORLD_70, K_WORLD_71 = SDLK_WORLD_71, K_WORLD_72 = SDLK_WORLD_72, K_WORLD_73 = SDLK_WORLD_73, K_WORLD_74 = SDLK_WORLD_74, K_WORLD_75 = SDLK_WORLD_75, K_WORLD_76 = SDLK_WORLD_76, K_WORLD_77 = SDLK_WORLD_77, K_WORLD_78 = SDLK_WORLD_78, K_WORLD_79 = SDLK_WORLD_79, K_WORLD_80 = SDLK_WORLD_80, K_WORLD_81 = SDLK_WORLD_81, K_WORLD_82 = SDLK_WORLD_82, K_WORLD_83 = SDLK_WORLD_83, K_WORLD_84 = SDLK_WORLD_84, K_WORLD_85 = SDLK_WORLD_85, K_WORLD_86 = SDLK_WORLD_86, K_WORLD_87 = SDLK_WORLD_87, K_WORLD_88 = SDLK_WORLD_88, K_WORLD_89 = SDLK_WORLD_89, K_WORLD_90 = SDLK_WORLD_90, K_WORLD_91 = SDLK_WORLD_91, K_WORLD_92 = SDLK_WORLD_92, K_WORLD_93 = SDLK_WORLD_93, K_WORLD_94 = SDLK_WORLD_94, K_WORLD_95 = SDLK_WORLD_95, // Numeric keypad K_KP0 = SDLK_KP0, K_KP1 = SDLK_KP1, K_KP2 = SDLK_KP2, K_KP3 = SDLK_KP3, K_KP4 = SDLK_KP4, K_KP5 = SDLK_KP5, K_KP6 = SDLK_KP6, K_KP7 = SDLK_KP7, K_KP8 = SDLK_KP8, K_KP9 = SDLK_KP9, K_KP_PERIOD = SDLK_KP_PERIOD, K_KP_DIVIDE = SDLK_KP_DIVIDE, K_KP_MULTIPLY = SDLK_KP_MULTIPLY, K_KP_MINUS = SDLK_KP_MINUS, K_KP_PLUS = SDLK_KP_PLUS, K_KP_ENTER = SDLK_KP_ENTER, K_KP_EQUALS = SDLK_KP_EQUALS, // Arrows + Home/End pad K_UP = SDLK_UP, K_DOWN = SDLK_DOWN, K_RIGHT = SDLK_RIGHT, K_LEFT = SDLK_LEFT, K_INSERT = SDLK_INSERT, K_HOME = SDLK_HOME, K_END = SDLK_END, K_PAGEUP = SDLK_PAGEUP, K_PAGEDOWN = SDLK_PAGEDOWN, // Function keys K_F1 = SDLK_F1, K_F2 = SDLK_F2, K_F3 = SDLK_F3, K_F4 = SDLK_F4, K_F5 = SDLK_F5, K_F6 = SDLK_F6, K_F7 = SDLK_F7, K_F8 = SDLK_F8, K_F9 = SDLK_F9, K_F10 = SDLK_F10, K_F11 = SDLK_F11, K_F12 = SDLK_F12, K_F13 = SDLK_F13, K_F14 = SDLK_F14, K_F15 = SDLK_F15, // Key state modifier keys K_NUMLOCK = SDLK_NUMLOCK, K_CAPSLOCK = SDLK_CAPSLOCK, K_SCROLLOCK = SDLK_SCROLLOCK, K_RSHIFT = SDLK_RSHIFT, K_LSHIFT = SDLK_LSHIFT, K_RCTRL = SDLK_RCTRL, K_LCTRL = SDLK_LCTRL, K_RALT = SDLK_RALT, K_LALT = SDLK_LALT, K_RMETA = SDLK_RMETA, K_LMETA = SDLK_LMETA, K_LSUPER = SDLK_LSUPER, // Left "Windows" key K_RSUPER = SDLK_RSUPER, // Right "Windows" key K_MODE = SDLK_MODE, // "Alt Gr" key K_COMPOSE = SDLK_COMPOSE, // Multi-key compose key // Miscellaneous function keys K_HELP = SDLK_HELP, K_PRINT = SDLK_PRINT, K_SYSREQ = SDLK_SYSREQ, K_BREAK = SDLK_BREAK, K_MENU = SDLK_MENU, K_POWER = SDLK_POWER, // Power Macintosh power key K_EURO = SDLK_EURO, // Some european keyboards K_UNDO = SDLK_UNDO, // Some japanese keyboard keys are unknown to SDL. // That is; they are all mapped to SDLKey=0 // However, they can recognized on their scancode // These keys are usefull for Japanese users who want to map // their host keyboard to the Japanese MSX keyboard // (e.g. the MSX turbo R keyboard) // Define some codes above suspected SDLKey value range, to // avoid clash with SDLKey values K_ZENKAKU_HENKAKU = 0x10000, // Enables EMI mode (MSX does this with CTRL+SPACE) K_MUHENKAN = 0x10001, // ??? K_HENKAN_MODE = 0x10002, // Similar to kanalock on MSX K_HIRAGANA_KATAKANA = 0x10003, // MSX switches between the two sets based on capslock state K_MASK = 0x1FFFF, // Modifiers KM_SHIFT = 0x020000, KM_CTRL = 0x040000, KM_ALT = 0x080000, KM_META = 0x100000, KM_MODE = 0x200000, // Direction modifiers KD_PRESS = 0, // key press KD_RELEASE = 0x400000 // key release }; /** * Translate key name to key code. * Returns K_NONE when the name is unknown. */ KeyCode getCode(string_ref name); KeyCode getCode(SDLKey key, SDLMod mod = KMOD_NONE, Uint8 scancode = 0, bool release = false); /** * Translate key code to key name. * Returns the string "unknown" for unknown key codes. */ const std::string getName(KeyCode keyCode); /** * Convenience method to create key combinations (hides ugly casts). */ KeyCode combine(KeyCode key, KeyCode modifier); } // namespace Keys } // namespace openmsx #endif openmsx-0.10.0/src/events/CliComm.cc0000644000175000017500000000131712262345041017773 0ustar manuelmanuel00000000000000#include "CliComm.hh" namespace openmsx { const char* const CliComm::levelStr[CliComm::NUM_LEVELS] = { "info", "warning", "error", "progress" }; const char* const CliComm::updateStr[CliComm::NUM_UPDATES] = { "led", "setting", "setting-info", "hardware", "plug", "unplug", "media", "status", "extension", "sounddevice", "connector" }; CliComm::CliComm() { } CliComm::~CliComm() { } void CliComm::printInfo(string_ref message) { log(INFO, message); } void CliComm::printWarning(string_ref message) { log(WARNING, message); } void CliComm::printError(string_ref message) { log(LOGLEVEL_ERROR, message); } void CliComm::printProgress(string_ref message) { log(PROGRESS, message); } } // namespace openmsx openmsx-0.10.0/src/events/MSXCliComm.cc0000644000175000017500000000133612262345041020364 0ustar manuelmanuel00000000000000#include "MSXCliComm.hh" #include "GlobalCliComm.hh" #include "MSXMotherBoard.hh" namespace openmsx { MSXCliComm::MSXCliComm(MSXMotherBoard& motherBoard_, GlobalCliComm& cliComm_) : motherBoard(motherBoard_) , cliComm(cliComm_) { } void MSXCliComm::log(LogLevel level, string_ref message) { cliComm.log(level, message); } void MSXCliComm::update(UpdateType type, string_ref name, string_ref value) { assert(type < NUM_UPDATES); auto it = prevValues[type].find(name); if (it != prevValues[type].end()) { if (it->second == value) { return; } it->second = value.str(); } else { prevValues[type][name] = value.str(); } cliComm.updateHelper(type, motherBoard.getMachineID(), name, value); } } // namespace openmsx openmsx-0.10.0/src/events/GlobalCliComm.hh0000644000175000017500000000164212262345041021127 0ustar manuelmanuel00000000000000#ifndef GLOBALCLICOMM_HH #define GLOBALCLICOMM_HH #include "CliComm.hh" #include "Semaphore.hh" #include "StringMap.hh" #include "noncopyable.hh" #include #include namespace openmsx { class CliListener; class GlobalCliComm : public CliComm, private noncopyable { public: GlobalCliComm(); virtual ~GlobalCliComm(); void addListener(CliListener* listener); void removeListener(CliListener* listener); // CliComm virtual void log(LogLevel level, string_ref message); virtual void update(UpdateType type, string_ref name, string_ref value); private: void updateHelper(UpdateType type, string_ref machine, string_ref name, string_ref value); StringMap prevValues[NUM_UPDATES]; std::vector listeners; Semaphore sem; // lock access to listeners member bool delivering; friend class MSXCliComm; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/FinishFrameEvent.hh0000644000175000017500000000352412262345041021661 0ustar manuelmanuel00000000000000#ifndef FINISHFRAMEEVENT_HH #define FINISHFRAMEEVENT_HH #include "Event.hh" #include "TclObject.hh" #include "checked_cast.hh" #include namespace openmsx { /** * This event is send when a device (v99x8, v9990, video9000, laserdisc) * reaches the end of a frame. This event has info on: * - which device generated the event * - which video layer was active * - was the frame actually rendered or not (frameskip) * Note that even if a frame was rendered (not skipped) it may not (need to) be * displayed because the corresponding video layer is not active. Or also even * if the corresponding video layer for a device is not active, the rendered * frame may still be displayed as part of a superimposed video layer. */ class FinishFrameEvent : public Event { public: FinishFrameEvent(int thisSource_, int selectedSource_, bool skipped_) : Event(OPENMSX_FINISH_FRAME_EVENT) , thisSource(thisSource_), selectedSource(selectedSource_) , skipped(skipped_) { } int getSource() const { return thisSource; } int getSelectedSource() const { return selectedSource; } bool isSkipped() const { return skipped; } bool needRender() const { return !skipped && (thisSource == selectedSource); } virtual void toStringImpl(TclObject& result) const { result.addListElement("finishframe"); result.addListElement(int(thisSource)); result.addListElement(int(selectedSource)); result.addListElement(skipped); } virtual bool lessImpl(const Event& other) const { auto& e = checked_cast(other); auto t1 = std::make_tuple( getSource(), getSelectedSource(), isSkipped()); auto t2 = std::make_tuple( e.getSource(), e.getSelectedSource(), e.isSkipped()); return t1 < t2; } private: const int thisSource; const int selectedSource; const bool skipped; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/Keys.cc0000644000175000017500000002327012262345041017365 0ustar manuelmanuel00000000000000#include "Keys.hh" #include "StringOp.hh" #include "stl.hh" #include #include #include using std::string; namespace openmsx { namespace Keys { static std::vector> keys; typedef CmpTupleElement<0, StringOp::caseless> CmpKeys; static void initialize() { static bool init = false; if (init) return; init = true; struct Entry { const char* name; KeyCode code; }; static const Entry entries[] = { { "BACKSPACE", K_BACKSPACE }, { "TAB", K_TAB }, { "CLEAR", K_CLEAR }, { "RETURN", K_RETURN }, { "PAUSE", K_PAUSE }, { "ESCAPE", K_ESCAPE }, { "SPACE", K_SPACE }, { "EXCLAIM", K_EXCLAIM }, { "QUOTEDBL", K_QUOTEDBL }, { "HASH", K_HASH }, { "DOLLAR", K_DOLLAR }, { "AMPERSAND", K_AMPERSAND }, { "QUOTE", K_QUOTE }, { "LEFTPAREN", K_LEFTPAREN }, { "RIGHTPAREN", K_RIGHTPAREN }, { "ASTERISK", K_ASTERISK }, { "PLUS", K_PLUS }, { "COMMA", K_COMMA }, { "MINUS", K_MINUS }, { "PERIOD", K_PERIOD }, { "SLASH", K_SLASH }, { "0", K_0 }, { "1", K_1 }, { "2", K_2 }, { "3", K_3 }, { "4", K_4 }, { "5", K_5 }, { "6", K_6 }, { "7", K_7 }, { "8", K_8 }, { "9", K_9 }, { "COLON", K_COLON }, { "SEMICOLON", K_SEMICOLON }, { "LESS", K_LESS }, { "EQUALS", K_EQUALS }, { "GREATER", K_GREATER }, { "QUESTION", K_QUESTION }, { "AT", K_AT }, { "LEFTBRACKET",K_LEFTBRACKET }, { "BACKSLASH", K_BACKSLASH }, { "RIGHTBRACKET",K_RIGHTBRACKET }, { "CARET", K_CARET }, { "UNDERSCORE", K_UNDERSCORE }, { "BACKQUOTE", K_BACKQUOTE }, { "A", K_A }, { "B", K_B }, { "C", K_C }, { "D", K_D }, { "E", K_E }, { "F", K_F }, { "G", K_G }, { "H", K_H }, { "I", K_I }, { "J", K_J }, { "K", K_K }, { "L", K_L }, { "M", K_M }, { "N", K_N }, { "O", K_O }, { "P", K_P }, { "Q", K_Q }, { "R", K_R }, { "S", K_S }, { "T", K_T }, { "U", K_U }, { "V", K_V }, { "W", K_W }, { "X", K_X }, { "Y", K_Y }, { "Z", K_Z }, { "DELETE", K_DELETE }, { "WORLD_0", K_WORLD_0 }, { "WORLD_1", K_WORLD_1 }, { "WORLD_2", K_WORLD_2 }, { "WORLD_3", K_WORLD_3 }, { "WORLD_4", K_WORLD_4 }, { "WORLD_5", K_WORLD_5 }, { "WORLD_6", K_WORLD_6 }, { "WORLD_7", K_WORLD_7 }, { "WORLD_8", K_WORLD_8 }, { "WORLD_9", K_WORLD_9 }, { "WORLD_10", K_WORLD_10 }, { "WORLD_11", K_WORLD_11 }, { "WORLD_12", K_WORLD_12 }, { "WORLD_13", K_WORLD_13 }, { "WORLD_14", K_WORLD_14 }, { "WORLD_15", K_WORLD_15 }, { "WORLD_16", K_WORLD_16 }, { "WORLD_17", K_WORLD_17 }, { "WORLD_18", K_WORLD_18 }, { "WORLD_19", K_WORLD_19 }, { "WORLD_20", K_WORLD_20 }, { "WORLD_21", K_WORLD_21 }, { "WORLD_22", K_WORLD_22 }, { "WORLD_23", K_WORLD_23 }, { "WORLD_24", K_WORLD_24 }, { "WORLD_25", K_WORLD_25 }, { "WORLD_26", K_WORLD_26 }, { "WORLD_27", K_WORLD_27 }, { "WORLD_28", K_WORLD_28 }, { "WORLD_29", K_WORLD_29 }, { "WORLD_30", K_WORLD_30 }, { "WORLD_31", K_WORLD_31 }, { "WORLD_32", K_WORLD_32 }, { "WORLD_33", K_WORLD_33 }, { "WORLD_34", K_WORLD_34 }, { "WORLD_35", K_WORLD_35 }, { "WORLD_36", K_WORLD_36 }, { "WORLD_37", K_WORLD_37 }, { "WORLD_38", K_WORLD_38 }, { "WORLD_39", K_WORLD_39 }, { "WORLD_40", K_WORLD_40 }, { "WORLD_41", K_WORLD_41 }, { "WORLD_42", K_WORLD_42 }, { "WORLD_43", K_WORLD_43 }, { "WORLD_44", K_WORLD_44 }, { "WORLD_45", K_WORLD_45 }, { "WORLD_46", K_WORLD_46 }, { "WORLD_47", K_WORLD_47 }, { "WORLD_48", K_WORLD_48 }, { "WORLD_49", K_WORLD_49 }, { "WORLD_50", K_WORLD_50 }, { "WORLD_51", K_WORLD_51 }, { "WORLD_52", K_WORLD_52 }, { "WORLD_53", K_WORLD_53 }, { "WORLD_54", K_WORLD_54 }, { "WORLD_55", K_WORLD_55 }, { "WORLD_56", K_WORLD_56 }, { "WORLD_57", K_WORLD_57 }, { "WORLD_58", K_WORLD_58 }, { "WORLD_59", K_WORLD_59 }, { "WORLD_60", K_WORLD_60 }, { "WORLD_61", K_WORLD_61 }, { "WORLD_62", K_WORLD_62 }, { "WORLD_63", K_WORLD_63 }, { "WORLD_64", K_WORLD_64 }, { "WORLD_65", K_WORLD_65 }, { "WORLD_66", K_WORLD_66 }, { "WORLD_67", K_WORLD_67 }, { "WORLD_68", K_WORLD_68 }, { "WORLD_69", K_WORLD_69 }, { "WORLD_70", K_WORLD_70 }, { "WORLD_71", K_WORLD_71 }, { "WORLD_72", K_WORLD_72 }, { "WORLD_73", K_WORLD_73 }, { "WORLD_74", K_WORLD_74 }, { "WORLD_75", K_WORLD_75 }, { "WORLD_76", K_WORLD_76 }, { "WORLD_77", K_WORLD_77 }, { "WORLD_78", K_WORLD_78 }, { "WORLD_79", K_WORLD_79 }, { "WORLD_80", K_WORLD_80 }, { "WORLD_81", K_WORLD_81 }, { "WORLD_82", K_WORLD_82 }, { "WORLD_83", K_WORLD_83 }, { "WORLD_84", K_WORLD_84 }, { "WORLD_85", K_WORLD_85 }, { "WORLD_86", K_WORLD_86 }, { "WORLD_87", K_WORLD_87 }, { "WORLD_88", K_WORLD_88 }, { "WORLD_89", K_WORLD_89 }, { "WORLD_90", K_WORLD_90 }, { "WORLD_91", K_WORLD_91 }, { "WORLD_92", K_WORLD_92 }, { "WORLD_93", K_WORLD_93 }, { "WORLD_94", K_WORLD_94 }, { "WORLD_95", K_WORLD_95 }, // Numeric keypad { "KP0", K_KP0 }, { "KP1", K_KP1 }, { "KP2", K_KP2 }, { "KP3", K_KP3 }, { "KP4", K_KP4 }, { "KP5", K_KP5 }, { "KP6", K_KP6 }, { "KP7", K_KP7 }, { "KP8", K_KP8 }, { "KP9", K_KP9 }, { "KP_PERIOD", K_KP_PERIOD }, { "KP_DIVIDE", K_KP_DIVIDE }, { "KP_MULTIPLY",K_KP_MULTIPLY }, { "KP_MINUS", K_KP_MINUS }, { "KP_PLUS", K_KP_PLUS }, { "KP_ENTER", K_KP_ENTER }, { "KP_EQUALS", K_KP_EQUALS }, // Arrows + Home/End pad { "UP", K_UP }, { "DOWN", K_DOWN }, { "RIGHT", K_RIGHT }, { "LEFT", K_LEFT }, { "INSERT", K_INSERT }, { "HOME", K_HOME }, { "END", K_END }, { "PAGEUP", K_PAGEUP }, { "PAGEDOWN", K_PAGEDOWN }, // Function keys { "F1", K_F1 }, { "F2", K_F2 }, { "F3", K_F3 }, { "F4", K_F4 }, { "F5", K_F5 }, { "F6", K_F6 }, { "F7", K_F7 }, { "F8", K_F8 }, { "F9", K_F9 }, { "F10", K_F10 }, { "F11", K_F11 }, { "F12", K_F12 }, { "F13", K_F13 }, { "F14", K_F14 }, { "F15", K_F15 }, // Key state modifier keys { "NUMLOCK", K_NUMLOCK }, { "CAPSLOCK", K_CAPSLOCK }, { "SCROLLOCK", K_SCROLLOCK }, { "RSHIFT", K_RSHIFT }, { "LSHIFT", K_LSHIFT }, { "RCTRL", K_RCTRL }, { "LCTRL", K_LCTRL }, { "RALT", K_RALT }, { "LALT", K_LALT }, { "RMETA", K_RMETA }, { "LMETA", K_LMETA }, { "LSUPER", K_LSUPER }, // Left "Windows" key { "RSUPER", K_RSUPER }, // Right "Windows" key { "RMODE", K_MODE }, // "Alt Gr" key { "COMPOSE", K_COMPOSE }, // Multi-key compose key // Miscellaneous function keys { "HELP", K_HELP }, { "PRINT", K_PRINT }, { "SYSREQ", K_SYSREQ }, { "BREAK", K_BREAK }, { "MENU", K_MENU }, { "POWER", K_POWER }, // Power Macintosh power key { "EURO", K_EURO }, // Some european keyboards { "UNDO", K_UNDO }, // Japanese keyboard special keys { "ZENKAKU_HENKAKU", K_ZENKAKU_HENKAKU }, { "MUHENKAN", K_MUHENKAN }, { "HENKAN_MODE", K_HENKAN_MODE }, { "HIRAGANA_KATAKANA", K_HIRAGANA_KATAKANA }, // Modifiers { "SHIFT", KM_SHIFT }, { "CTRL", KM_CTRL }, { "ALT", KM_ALT }, { "META", KM_META }, { "MODE", KM_MODE }, // Direction modifiers { "PRESS", KD_PRESS }, { "RELEASE", KD_RELEASE }, { nullptr, K_NONE } }; const Entry* e = entries; while (e->name) { keys.push_back(std::make_pair(e->name, e->code)); ++e; } sort(keys.begin(), keys.end(), CmpKeys()); } KeyCode getCode(string_ref name) { initialize(); auto result = static_cast(0); string_ref::size_type lastPos = 0; while (lastPos != string_ref::npos) { auto pos = name.substr(lastPos).find_first_of(",+/"); auto part = (pos != string_ref::npos) ? name.substr(lastPos, pos) : name.substr(lastPos); auto it = lower_bound(keys.begin(), keys.end(), part, CmpKeys()); StringOp::casecmp cmp; if ((it == keys.end()) || !cmp(it->first, part)) { return K_NONE; } KeyCode partCode = it->second; if ((partCode & K_MASK) && (result & K_MASK)) { // more than one non-modifier component // is not allowed return K_NONE; } result = static_cast(result | partCode); lastPos = (pos != string_ref::npos) ? lastPos + pos + 1 : string_ref::npos; } return result; } KeyCode getCode(SDLKey key, SDLMod mod, Uint8 scancode, bool release) { auto result = static_cast(key); if (result == 0) { // Assume it is a japanese keyboard and check // scancode to recognize a few japanese // specific keys for which SDL does not have an // SDLKey keysym definition. switch (scancode) { case 49: result = static_cast(K_ZENKAKU_HENKAKU); break; case 129: result = static_cast(K_HENKAN_MODE); break; case 131: result = static_cast(K_MUHENKAN); break; case 208: result = static_cast(K_HIRAGANA_KATAKANA); break; } } if (mod & KMOD_CTRL) { result = static_cast(result | KM_CTRL); } if (mod & KMOD_SHIFT) { result = static_cast(result | KM_SHIFT); } if (mod & KMOD_ALT) { result = static_cast(result | KM_ALT); } if (mod & KMOD_META) { result = static_cast(result | KM_META); } if (mod & KMOD_MODE) { result = static_cast(result | KM_MODE); } if (release) { result = static_cast(result | KD_RELEASE); } return result; } const string getName(KeyCode keyCode) { initialize(); string result; for (auto& p : keys) { if (p.second == (keyCode & K_MASK)) { result = p.first.str(); break; } } if (result.empty()) { return "unknown"; } if (keyCode & KM_CTRL) { result += "+CTRL"; } if (keyCode & KM_SHIFT) { result += "+SHIFT"; } if (keyCode & KM_ALT) { result += "+ALT"; } if (keyCode & KM_META) { result += "+META"; } if (keyCode & KM_MODE) { result += "+MODE"; } if (keyCode & KD_RELEASE) { result += ",RELEASE"; } return result; } KeyCode combine(KeyCode key, KeyCode modifier) { return static_cast(int(key) | int(modifier)); } } // namespace Keys } // namespace openmsx openmsx-0.10.0/src/events/GlobalCliComm.cc0000644000175000017500000000447712262345041021126 0ustar manuelmanuel00000000000000#include "GlobalCliComm.hh" #include "CliListener.hh" #include "Thread.hh" #include "ScopedAssign.hh" #include #include #include namespace openmsx { GlobalCliComm::GlobalCliComm() : sem(1) , delivering(false) { } GlobalCliComm::~GlobalCliComm() { assert(Thread::isMainThread()); assert(!delivering); ScopedLock lock(sem); // TODO GlobalCliComm has unusual ownership semantics. // Try to rework it. for (auto& l : listeners) { delete l; } } void GlobalCliComm::addListener(CliListener* listener) { // can be called from any thread ScopedLock lock(sem); listeners.push_back(listener); } void GlobalCliComm::removeListener(CliListener* listener) { // can be called from any thread ScopedLock lock(sem); auto it = find(listeners.begin(), listeners.end(), listener); assert(it != listeners.end()); listeners.erase(it); } void GlobalCliComm::log(LogLevel level, string_ref message) { assert(Thread::isMainThread()); if (delivering) { // Don't allow recursive calls, this would hang while trying to // acquire the Semaphore below. But also when we would change // this to a recursive-mutex, this could result in an infinite // loop. // One example of a recursive invocation is when something goes // wrong in the Tcl proc attached to message_callback (e.g. the // font used to display the message could not be loaded). std::cerr << "Recursive cliComm message: " << message << std::endl; return; } ScopedAssign sa(delivering, true); ScopedLock lock(sem); if (!listeners.empty()) { for (auto& l : listeners) { l->log(level, message); } } else { // don't let the message get lost std::cerr << message << std::endl; } } void GlobalCliComm::update(UpdateType type, string_ref name, string_ref value) { assert(type < NUM_UPDATES); auto it = prevValues[type].find(name); if (it != prevValues[type].end()) { if (it->second == value) { return; } it->second = value.str(); } else { prevValues[type][name] = value.str(); } updateHelper(type, "", name, value); } void GlobalCliComm::updateHelper(UpdateType type, string_ref machine, string_ref name, string_ref value) { assert(Thread::isMainThread()); ScopedLock lock(sem); for (auto& l : listeners) { l->update(type, machine, name, value); } } } // namespace openmsx openmsx-0.10.0/src/events/InputEventFactory.cc0000644000175000017500000001556412262345041022112 0ustar manuelmanuel00000000000000#include "InputEventFactory.hh" #include "InputEvents.hh" #include "CommandException.hh" #include "StringOp.hh" #include "Interpreter.hh" #include "openmsx.hh" using std::string; using std::vector; using std::make_shared; namespace openmsx { namespace InputEventFactory { static EventPtr parseKeyEvent(const string& str, const int unicode) { Keys::KeyCode keyCode = Keys::getCode(str); if (keyCode == Keys::K_NONE) { throw CommandException("Invalid keycode: " + str); } if (keyCode & Keys::KD_RELEASE) { return make_shared(keyCode, unicode); } else { return make_shared(keyCode, unicode); } } static EventPtr parseKeyEvent( const string& str, const vector& components) { if (components.size() == 2) { return parseKeyEvent(components[1], 0); } else if ((components.size() == 3) && (StringOp::startsWith(components[2], "unicode"))) { return parseKeyEvent(components[1], stoi(string_ref(components[2]).substr(7))); } else { throw CommandException("Invalid keyboard event: " + str); } } static bool upDown(const string& str) { if (str == "up") { return true; } else if (str == "down") { return false; } else { throw CommandException("Invalid direction (expected 'up' or 'down'): " + str); } } static EventPtr parseMouseEvent( const string& str, const vector& components) { if (components.size() < 2) { throw CommandException("Invalid mouse event: " + str); } if (components[1] == "motion") { if (components.size() == 2) { return make_shared(); } if ((components.size() != 4) && (components.size() != 6)) { throw CommandException("Invalid mouse motion event: " + str); } int absX, absY; if (components.size() == 6) { absX = StringOp::stringToInt(components[4]), absY = StringOp::stringToInt(components[5]); } else { // for bw-compat also allow events without absX,absY absX = absY = 0; } return make_shared( StringOp::stringToInt(components[2]), StringOp::stringToInt(components[3]), absX, absY); } else if (StringOp::startsWith(components[1], "button")) { if (components.size() != 3) { throw CommandException("Invalid mouse button event: " + str); } unsigned button = stoi(string_ref(components[1]).substr(6)); if (upDown(components[2])) { return make_shared (button); } else { return make_shared(button); } } else { throw CommandException("Invalid mouse event: " + str); } } static EventPtr parseOsdControlEvent( const string& str, const vector& components) { #ifdef DEBUG ad_printf("components.size(): %d\n", components.size()); for (unsigned cnt=0; cnt != components.size(); cnt++) { ad_printf("component[%d]: %s\n", cnt, components[cnt].c_str()); } #endif if (components.size() != 3) { throw CommandException("Invalid OSDcontrol event: " + str); } string buttonName = components[1]; unsigned button; if (buttonName == "LEFT") { button = OsdControlEvent::LEFT_BUTTON; } else if (buttonName == "RIGHT") { button = OsdControlEvent::RIGHT_BUTTON; } else if (buttonName == "UP") { button = OsdControlEvent::UP_BUTTON; } else if (buttonName == "DOWN") { button = OsdControlEvent::DOWN_BUTTON; } else if (buttonName == "A") { button = OsdControlEvent::A_BUTTON; } else if (buttonName == "B") { button = OsdControlEvent::B_BUTTON; } else { throw CommandException("Invalid OSDcontrol event: " + str); } if (components[2] == "RELEASE") { return make_shared(button, nullptr); } else if (components[2] == "PRESS") { return make_shared(button, nullptr); } else { throw CommandException("Invalid OSDcontrol event: " + str); } } static EventPtr parseJoystickEvent( const string& str, const vector& components) { if (components.size() != 3) { throw CommandException("Invalid joystick event: " + str); } int joystick; string joyString = components[0].substr(3); if (!StringOp::stringToInt(joyString, joystick) || (joystick == 0)) { throw CommandException("Invalid joystick number: " + joyString); } --joystick; if (StringOp::startsWith(components[1], "button")) { int button; string joyButtonString = components[1].substr(6); if (!StringOp::stringToInt(joyButtonString, button)) { throw CommandException("Invalid joystick button number: " + joyButtonString); } if (upDown(components[2])) { return make_shared(joystick, button); } else { return make_shared(joystick, button); } } else if (StringOp::startsWith(components[1], "axis")) { int axis; string axisString = components[1].substr(4); if (!StringOp::stringToInt(axisString, axis)) { throw CommandException("Invalid axis number: " + axisString); } int value; if (!StringOp::stringToInt(components[2], value) || (short(value) != value)) { throw CommandException("Invalid value: " + components[2]); } return make_shared(joystick, axis, value); } else { throw CommandException("Invalid joystick event: " + str); } } static EventPtr parseFocusEvent( const string& str, const vector& components) { if (components.size() != 2) { throw CommandException("Invalid focus event: " + str); } bool gain = StringOp::stringToBool(components[1]); return make_shared(gain); } static EventPtr parseResizeEvent( const string& str, const vector& components) { if (components.size() != 3) { throw CommandException("Invalid resize event: " + str); } int x = StringOp::stringToInt(components[1]); int y = StringOp::stringToInt(components[2]); return make_shared(x, y); } static EventPtr parseQuitEvent( const string& str, const vector& components) { if (components.size() != 1) { throw CommandException("Invalid quit event: " + str); } return make_shared(); } EventPtr createInputEvent(const string& str) { auto components = Interpreter::splitList(str, nullptr); if (components.empty()) { throw CommandException("Invalid event: \"" + str + '\"'); } if (components[0] == "keyb") { return parseKeyEvent(str, components); } else if (components[0] == "mouse") { return parseMouseEvent(str, components); } else if (StringOp::startsWith(components[0], "joy")) { return parseJoystickEvent(str, components); } else if (components[0] == "focus") { return parseFocusEvent(str, components); } else if (components[0] == "resize") { return parseResizeEvent(str, components); } else if (components[0] == "quit") { return parseQuitEvent(str, components); } else if (components[0] == "command") { return EventPtr(); //return parseCommandEvent(str, components); } else if (components[0] == "OSDcontrol") { return parseOsdControlEvent(str, components); } else { // fall back return parseKeyEvent(components[0], 0); } } } // namespace InputEventFactory } // namespace openmsx openmsx-0.10.0/src/events/EventDistributor.hh0000644000175000017500000000461212262345041021777 0ustar manuelmanuel00000000000000#ifndef EVENTDISTRIBUTOR_HH #define EVENTDISTRIBUTOR_HH #include "Event.hh" #include "Semaphore.hh" #include "CondVar.hh" #include "noncopyable.hh" #include #include #include namespace openmsx { class Reactor; class EventListener; class EventDistributor : private noncopyable { public: typedef std::shared_ptr EventPtr; /** Priorities from high to low, higher priority listeners can block * events for lower priority listeners. */ enum Priority { OTHER, CONSOLE, HOTKEY, MSX, }; explicit EventDistributor(Reactor& reactor); /** * Registers a given object to receive certain events. * @param type The type of the events you want to receive. * @param listener Listener that will be notified when an event arrives. * @param priority Listeners have a priority, higher priority liseners * can block events for lower priority listeners. */ void registerEventListener(EventType type, EventListener& listener, Priority priority = OTHER); /** * Unregisters a previously registered event listener. * @param type The type of the events the listener should no longer receive. * @param listener Listener to unregister. */ void unregisterEventListener(EventType type, EventListener& listener); /** Schedule the given event for delivery. Actual delivery happens * when the deliverEvents() method is called. Events are always * in the main thread. */ void distributeEvent(const EventPtr& event); /** This actually delivers the events. It may only be called from the * main loop in Reactor (and only from the main thread). Also see * the distributeEvent() method. */ void deliverEvents(); /** Sleep for the specified amount of time, but return early when * (another thread) called the distributeEvent() method. * @param us Amount of time to sleep, in micro seconds. * @result true if we return because time has passed * false if we return because distributeEvent() was called */ bool sleep(unsigned us); private: bool isRegistered(EventType type, EventListener* listener) const; Reactor& reactor; typedef std::vector> PriorityMap; std::vector listeners; // indexed by EventType typedef std::vector EventQueue; EventQueue scheduledEvents; Semaphore sem; CondVar cond; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/StdioMessages.cc0000644000175000017500000000077212262345041021226 0ustar manuelmanuel00000000000000#include "StdioMessages.hh" #include using std::string; namespace openmsx { void StdioMessages::log(CliComm::LogLevel level, string_ref message) { auto levelStr = CliComm::getLevelStrings(); ((level == CliComm::INFO) ? std::cout : std::cerr) << levelStr[level] << ": " << message << std::endl; } void StdioMessages::update(CliComm::UpdateType /*type*/, string_ref /*machine*/, string_ref /*name*/, string_ref /*value*/) { // ignore } } // namespace openmsx openmsx-0.10.0/src/events/AfterCommand.cc0000644000175000017500000004231312262345041021011 0ustar manuelmanuel00000000000000#include "AfterCommand.hh" #include "CommandController.hh" #include "CliComm.hh" #include "Schedulable.hh" #include "EventDistributor.hh" #include "EventDistributor.hh" #include "InputEventFactory.hh" #include "Reactor.hh" #include "MSXMotherBoard.hh" #include "Alarm.hh" #include "EmuTime.hh" #include "CommandException.hh" #include "Interpreter.hh" #include "TclObject.hh" #include "StringOp.hh" #include "openmsx.hh" #include "unreachable.hh" #include "memory.hh" #include #include #include #include using std::ostringstream; using std::string; using std::vector; using std::unique_ptr; using std::move; namespace openmsx { class AfterCmd { public: virtual ~AfterCmd(); string_ref getCommand() const; const string& getId() const; virtual string getType() const = 0; void execute(); protected: AfterCmd(AfterCommand& afterCommand, const TclObject& command); unique_ptr removeSelf(); AfterCommand& afterCommand; TclObject command; string id; static unsigned lastAfterId; }; class AfterTimedCmd : public AfterCmd, private Schedulable { public: double getTime() const; void reschedule(); protected: AfterTimedCmd(Scheduler& scheduler, AfterCommand& afterCommand, const TclObject& command, double time); private: virtual void executeUntil(EmuTime::param time, int userData); virtual void schedulerDeleted(); double time; // Zero when expired, otherwise the original duration (to // be able to reschedule for 'after idle'). }; class AfterTimeCmd : public AfterTimedCmd { public: AfterTimeCmd(Scheduler& scheduler, AfterCommand& afterCommand, const TclObject& command, double time); virtual string getType() const; }; class AfterIdleCmd : public AfterTimedCmd { public: AfterIdleCmd(Scheduler& scheduler, AfterCommand& afterCommand, const TclObject& command, double time); virtual string getType() const; }; template class AfterEventCmd : public AfterCmd { public: AfterEventCmd(AfterCommand& afterCommand, const TclObject& type, const TclObject& command); virtual string getType() const; private: const string type; }; class AfterInputEventCmd : public AfterCmd { public: AfterInputEventCmd(AfterCommand& afterCommand, const AfterCommand::EventPtr& event, const TclObject& command); virtual string getType() const; AfterCommand::EventPtr getEvent() const { return event; } private: AfterCommand::EventPtr event; }; class AfterRealTimeCmd : public AfterCmd, private Alarm { public: AfterRealTimeCmd(AfterCommand& afterCommand, const TclObject& command, double time); virtual ~AfterRealTimeCmd(); virtual string getType() const; bool hasExpired() const; private: virtual bool alarm(); bool expired; }; AfterCommand::AfterCommand(Reactor& reactor_, EventDistributor& eventDistributor_, CommandController& commandController) : Command(commandController, "after") , reactor(reactor_) , eventDistributor(eventDistributor_) { // TODO DETACHED <-> EMU types should be cleaned up // (moved to event iso listener?) eventDistributor.registerEventListener( OPENMSX_KEY_UP_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_KEY_DOWN_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MOUSE_MOTION_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_UP_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_JOY_AXIS_MOTION_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_UP_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_JOY_BUTTON_DOWN_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_FINISH_FRAME_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_BREAK_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_QUIT_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_BOOT_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_MACHINE_LOADED_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_AFTER_REALTIME_EVENT, *this); eventDistributor.registerEventListener( OPENMSX_AFTER_TIMED_EVENT, *this); } AfterCommand::~AfterCommand() { eventDistributor.unregisterEventListener( OPENMSX_AFTER_TIMED_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_AFTER_REALTIME_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MACHINE_LOADED_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_BOOT_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_QUIT_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_BREAK_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_FINISH_FRAME_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_BUTTON_UP_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_JOY_AXIS_MOTION_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_BUTTON_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_BUTTON_UP_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_MOUSE_MOTION_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_KEY_DOWN_EVENT, *this); eventDistributor.unregisterEventListener( OPENMSX_KEY_UP_EVENT, *this); } void AfterCommand::execute(const vector& tokens, TclObject& result) { if (tokens.size() < 2) { throw CommandException("Missing argument"); } int time; string_ref subCmd = tokens[1].getString(); if (subCmd == "time") { afterTime(tokens, result); } else if (subCmd == "realtime") { afterRealTime(tokens, result); } else if (subCmd == "idle") { afterIdle(tokens, result); } else if (subCmd == "frame") { afterEvent(tokens, result); } else if (subCmd == "break") { afterEvent(tokens, result); } else if (subCmd == "quit") { afterEvent(tokens, result); } else if (subCmd == "boot") { afterEvent(tokens, result); } else if (subCmd == "machine_switch") { afterEvent(tokens, result); } else if (subCmd == "info") { afterInfo(tokens, result); } else if (subCmd == "cancel") { afterCancel(tokens, result); } else if (StringOp::stringToInt(subCmd.str(), time)) { // TODO afterTclTime(time, tokens, result); } else { // try to interpret token as an event name try { afterInputEvent( InputEventFactory::createInputEvent(subCmd.str()), // TODO tokens, result); } catch (MSXException&) { throw SyntaxError(); } } } static double getTime(const TclObject& obj) { double time = obj.getDouble(); if (time < 0) { throw CommandException("Not a valid time specification"); } return time; } void AfterCommand::afterTime(const vector& tokens, TclObject& result) { if (tokens.size() != 4) { throw SyntaxError(); } MSXMotherBoard* motherBoard = reactor.getMotherBoard(); if (!motherBoard) return; double time = getTime(tokens[2]); auto cmd = make_unique( motherBoard->getScheduler(), *this, tokens[3], time); result.setString(cmd->getId()); afterCmds.push_back(move(cmd)); } void AfterCommand::afterRealTime(const vector& tokens, TclObject& result) { if (tokens.size() != 4) { throw SyntaxError(); } double time = getTime(tokens[2]); auto cmd = make_unique( *this, tokens[3], time); result.setString(cmd->getId()); afterCmds.push_back(move(cmd)); } void AfterCommand::afterTclTime( int ms, const vector& tokens, TclObject& result) { TclObject command(tokens.front().getInterpreter()); command.addListElements(tokens.begin() + 2, tokens.end()); auto cmd = make_unique( *this, command, ms / 1000.0); result.setString(cmd->getId()); afterCmds.push_back(move(cmd)); } template void AfterCommand::afterEvent(const vector& tokens, TclObject& result) { if (tokens.size() != 3) { throw SyntaxError(); } auto cmd = make_unique>( *this, tokens[1], tokens[2]); result.setString(cmd->getId()); afterCmds.push_back(move(cmd)); } void AfterCommand::afterInputEvent( const EventPtr& event, const vector& tokens, TclObject& result) { if (tokens.size() != 3) { throw SyntaxError(); } auto cmd = make_unique( *this, event, tokens[2]); result.setString(cmd->getId()); afterCmds.push_back(move(cmd)); } void AfterCommand::afterIdle(const vector& tokens, TclObject& result) { if (tokens.size() != 4) { throw SyntaxError(); } MSXMotherBoard* motherBoard = reactor.getMotherBoard(); if (!motherBoard) return; double time = getTime(tokens[2]); auto cmd = make_unique( motherBoard->getScheduler(), *this, tokens[3], time); result.setString(cmd->getId()); afterCmds.push_back(move(cmd)); } void AfterCommand::afterInfo(const vector& /*tokens*/, TclObject& result) { ostringstream str; for (auto& cmd : afterCmds) { str << cmd->getId() << ": "; str << cmd->getType() << ' '; if (auto cmd2 = dynamic_cast(cmd.get())) { str.precision(3); str << std::fixed << std::showpoint << cmd2->getTime() << ' '; } str << cmd->getCommand() << '\n'; } result.setString(str.str()); } void AfterCommand::afterCancel(const vector& tokens, TclObject& /*result*/) { if (tokens.size() != 3) { throw SyntaxError(); } if (tokens.size() == 3) { for (auto it = afterCmds.begin(); it != afterCmds.end(); ++it) { if ((*it)->getId() == tokens[2].getString()) { afterCmds.erase(it); return; } } } TclObject command; command.addListElements(tokens.begin() + 2, tokens.end()); string_ref cmdStr = command.getString(); for (auto it = afterCmds.begin(); it != afterCmds.end(); ++it) { if ((*it)->getCommand() == cmdStr) { afterCmds.erase(it); // Tcl manual is not clear about this, but it seems // there's only occurence of this command canceled. // It's also not clear which of the (possibly) several // matches is canceled. return; } } // It's not an error if no match is found } string AfterCommand::help(const vector& /*tokens*/) const { return "after time execute a command after some time (MSX time)\n" "after realtime execute a command after some time (realtime)\n" "after idle execute a command after some time being idle\n" "after frame execute a command after a new frame is drawn\n" "after break execute a command after a breakpoint is reached\n" "after boot execute a command after a (re)boot\n" "after machine_switch execute a command after a switch to a new machine\n" "after info list all postponed commands\n" "after cancel cancel the postponed command with given id\n"; } void AfterCommand::tabCompletion(vector& tokens) const { if (tokens.size() == 2) { static const char* const cmds[] = { "time", "realtime", "idle", "frame", "break", "boot", "machine_switch", "info", "cancel", }; completeString(tokens, cmds); } // TODO : make more complete } template void AfterCommand::executeMatches(PRED pred) { // predicate should return false on matches auto it = partition(afterCmds.begin(), afterCmds.end(), pred); AfterCmds tmp(std::make_move_iterator(it), std::make_move_iterator(afterCmds.end())); afterCmds.erase(it, afterCmds.end()); for (auto& c : tmp) { c->execute(); } } template struct AfterEventPred { bool operator()(const unique_ptr& x) const { return !dynamic_cast*>(x.get()); } }; template void AfterCommand::executeEvents() { executeMatches(AfterEventPred()); } struct AfterTimePred { bool operator()(const unique_ptr& x) const { if (auto* cmd = dynamic_cast(x.get())) { if (cmd->hasExpired()) { return false; } } return true; } }; struct AfterEmuTimePred { bool operator()(const unique_ptr& x) const { if (auto* cmd = dynamic_cast(x.get())) { if (cmd->getTime() == 0.0) { return false; } } return true; } }; struct AfterInputEventPred { AfterInputEventPred(const AfterCommand::EventPtr& event_) : event(event_) {} bool operator()(const unique_ptr& x) const { if (auto* cmd = dynamic_cast(x.get())) { if (cmd->getEvent()->matches(*event)) return false; } return true; } AfterCommand::EventPtr event; }; int AfterCommand::signalEvent(const std::shared_ptr& event) { if (event->getType() == OPENMSX_FINISH_FRAME_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_BREAK_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_BOOT_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_QUIT_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_MACHINE_LOADED_EVENT) { executeEvents(); } else if (event->getType() == OPENMSX_AFTER_REALTIME_EVENT) { executeMatches(AfterTimePred()); } else if (event->getType() == OPENMSX_AFTER_TIMED_EVENT) { executeMatches(AfterEmuTimePred()); } else { executeMatches(AfterInputEventPred(event)); for (auto& c : afterCmds) { if (auto* cmd = dynamic_cast(c.get())) { cmd->reschedule(); } } } return 0; } // class AfterCmd unsigned AfterCmd::lastAfterId = 0; AfterCmd::AfterCmd(AfterCommand& afterCommand_, const TclObject& command_) : afterCommand(afterCommand_), command(command_) { ostringstream str; str << "after#" << ++lastAfterId; id = str.str(); } AfterCmd::~AfterCmd() { } string_ref AfterCmd::getCommand() const { return command.getString(); } const string& AfterCmd::getId() const { return id; } void AfterCmd::execute() { try { command.executeCommand(); } catch (CommandException& e) { afterCommand.getCommandController().getCliComm().printWarning( "Error executing delayed command: " + e.getMessage()); } } unique_ptr AfterCmd::removeSelf() { for (auto it = afterCommand.afterCmds.begin(); it != afterCommand.afterCmds.end(); ++it) { if (it->get() == this) { auto result = move(*it); afterCommand.afterCmds.erase(it); return result; } } UNREACHABLE; return nullptr; } // class AfterTimedCmd AfterTimedCmd::AfterTimedCmd( Scheduler& scheduler, AfterCommand& afterCommand, const TclObject& command, double time_) : AfterCmd(afterCommand, command) , Schedulable(scheduler) , time(time_) { reschedule(); } double AfterTimedCmd::getTime() const { return time; } void AfterTimedCmd::reschedule() { removeSyncPoint(); setSyncPoint(getCurrentTime() + EmuDuration(time)); } void AfterTimedCmd::executeUntil(EmuTime::param /*time*/, int /*userData*/) { time = 0.0; // execute on next event afterCommand.eventDistributor.distributeEvent( std::make_shared(OPENMSX_AFTER_TIMED_EVENT)); } void AfterTimedCmd::schedulerDeleted() { removeSelf(); } // class AfterTimeCmd AfterTimeCmd::AfterTimeCmd( Scheduler& scheduler, AfterCommand& afterCommand, const TclObject& command, double time) : AfterTimedCmd(scheduler, afterCommand, command, time) { } string AfterTimeCmd::getType() const { return "time"; } // class AfterIdleCmd AfterIdleCmd::AfterIdleCmd( Scheduler& scheduler, AfterCommand& afterCommand, const TclObject& command, double time) : AfterTimedCmd(scheduler, afterCommand, command, time) { } string AfterIdleCmd::getType() const { return "idle"; } // class AfterEventCmd template AfterEventCmd::AfterEventCmd( AfterCommand& afterCommand, const TclObject& type_, const TclObject& command) : AfterCmd(afterCommand, command), type(type_.getString().str()) { } template string AfterEventCmd::getType() const { return type; } // AfterInputEventCmd AfterInputEventCmd::AfterInputEventCmd( AfterCommand& afterCommand, const AfterCommand::EventPtr& event_, const TclObject& command) : AfterCmd(afterCommand, command) , event(event_) { } string AfterInputEventCmd::getType() const { return event->toString(); } // class AfterRealTimeCmd AfterRealTimeCmd::AfterRealTimeCmd( AfterCommand& afterCommand, const TclObject& command, double time) : AfterCmd(afterCommand, command) , expired(false) { schedule(unsigned(time * 1000000)); // micro seconds } AfterRealTimeCmd::~AfterRealTimeCmd() { prepareDelete(); } string AfterRealTimeCmd::getType() const { return "realtime"; } bool AfterRealTimeCmd::alarm() { // this runs in a different thread, so we can't directly execute the // command here expired = true; afterCommand.eventDistributor.distributeEvent( std::make_shared(OPENMSX_AFTER_REALTIME_EVENT)); return false; // don't repeat alarm } bool AfterRealTimeCmd::hasExpired() const { return expired; } } // namespace openmsx openmsx-0.10.0/src/events/InputEvents.hh0000644000175000017500000001523312262345041020750 0ustar manuelmanuel00000000000000#ifndef INPUTEVENTS_HH #define INPUTEVENTS_HH #include "openmsx.hh" #include "Event.hh" #include "Keys.hh" #include #include #include namespace openmsx { class TimedEvent : public Event { public: /** Query creation time. */ uint64_t getRealTime() const { return realtime; } protected: explicit TimedEvent(EventType type); private: const uint64_t realtime; }; class KeyEvent : public TimedEvent { public: Keys::KeyCode getKeyCode() const { return keyCode; } uint16_t getUnicode() const { return unicode; } protected: KeyEvent(EventType type, Keys::KeyCode keyCode, uint16_t unicode); private: virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const Event& other) const; const Keys::KeyCode keyCode; const uint16_t unicode; }; class KeyUpEvent : public KeyEvent { public: explicit KeyUpEvent(Keys::KeyCode keyCode); KeyUpEvent(Keys::KeyCode keyCode, uint16_t unicode); }; class KeyDownEvent : public KeyEvent { public: explicit KeyDownEvent(Keys::KeyCode keyCode); KeyDownEvent(Keys::KeyCode keyCode, uint16_t unicode); }; class MouseButtonEvent : public TimedEvent { public: static const unsigned LEFT = 1; static const unsigned MIDDLE = 2; static const unsigned RIGHT = 3; static const unsigned WHEELUP = 4; static const unsigned WHEELDOWN = 5; unsigned getButton() const { return button; } protected: MouseButtonEvent(EventType type, unsigned button_); void toStringHelper(TclObject& result) const; private: virtual bool lessImpl(const Event& other) const; const unsigned button; }; class MouseButtonUpEvent : public MouseButtonEvent { public: explicit MouseButtonUpEvent(unsigned button); private: virtual void toStringImpl(TclObject& result) const; }; class MouseButtonDownEvent : public MouseButtonEvent { public: explicit MouseButtonDownEvent(unsigned button); private: virtual void toStringImpl(TclObject& result) const; }; class MouseMotionEvent : public TimedEvent { public: MouseMotionEvent(int xrel, int yrel, int xabs, int yabs); int getX() const { return xrel; } int getY() const { return yrel; } int getAbsX() const { return xabs; } int getAbsY() const { return yabs; } private: virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const Event& other) const; const int xrel; const int yrel; const int xabs; const int yabs; }; class MouseMotionGroupEvent : public Event { public: MouseMotionGroupEvent(); private: virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const Event& other) const; virtual bool matches(const Event& other) const; }; class JoystickEvent : public TimedEvent { public: unsigned getJoystick() const { return joystick; } protected: JoystickEvent(EventType type, unsigned joystick); void toStringHelper(TclObject& result) const; private: virtual bool lessImpl(const Event& other) const; virtual bool lessImpl(const JoystickEvent& other) const = 0; const unsigned joystick; }; class JoystickButtonEvent : public JoystickEvent { public: unsigned getButton() const { return button; } protected: JoystickButtonEvent(EventType type, unsigned joystick, unsigned button); void toStringHelper(TclObject& result) const; private: virtual bool lessImpl(const JoystickEvent& other) const; const unsigned button; }; class JoystickButtonUpEvent : public JoystickButtonEvent { public: JoystickButtonUpEvent(unsigned joystick, unsigned button); private: virtual void toStringImpl(TclObject& result) const; }; class JoystickButtonDownEvent : public JoystickButtonEvent { public: JoystickButtonDownEvent(unsigned joystick, unsigned button); private: virtual void toStringImpl(TclObject& result) const; }; class JoystickAxisMotionEvent : public JoystickEvent { public: static const unsigned X_AXIS = 0; static const unsigned Y_AXIS = 1; JoystickAxisMotionEvent(unsigned joystick, unsigned axis, short value); unsigned getAxis() const { return axis; } short getValue() const { return value; } private: virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const JoystickEvent& other) const; const unsigned axis; const short value; }; class FocusEvent : public Event { public: explicit FocusEvent(bool gain); bool getGain() const { return gain; } private: virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const Event& other) const; const bool gain; }; class ResizeEvent : public Event { public: ResizeEvent(unsigned x, unsigned y); unsigned getX() const { return x; } unsigned getY() const { return y; } private: virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const Event& other) const; const unsigned x; const unsigned y; }; class QuitEvent : public Event { public: QuitEvent(); private: virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const Event& other) const; }; /** OSD events are triggered by other events. They aggregate keyboard and * joystick events into one set of events that can be used to e.g. control * OSD elements. */ class OsdControlEvent : public TimedEvent { public: enum { LEFT_BUTTON, RIGHT_BUTTON, UP_BUTTON, DOWN_BUTTON, A_BUTTON, B_BUTTON }; unsigned getButton() const { return button; } /** Get the event that actually triggered the creation of this event. * Typically this will be a keyboard or joystick event. This could * also return nullptr (after a toString/fromString conversion). * For the current use (key-repeat) this is ok. */ /** Normally all events should stop the repeat process in 'bind -repeat', * but in case of OsdControlEvent there are two exceptions: * - we should not stop because of the original host event that * actually generated this 'artificial' OsdControlEvent. * - if the original host event is a joystick motion event, we * should not stop repeat for 'small' relative new joystick events. */ virtual bool isRepeatStopper(const Event& other) const; protected: OsdControlEvent(EventType type, unsigned button_, const std::shared_ptr& origEvent); void toStringHelper(TclObject& result) const; private: virtual bool lessImpl(const Event& other) const; const std::shared_ptr origEvent; const unsigned button; }; class OsdControlReleaseEvent : public OsdControlEvent { public: OsdControlReleaseEvent(unsigned button, const std::shared_ptr& origEvent); private: virtual void toStringImpl(TclObject& result) const; }; class OsdControlPressEvent : public OsdControlEvent { public: OsdControlPressEvent(unsigned button, const std::shared_ptr& origEvent); private: virtual void toStringImpl(TclObject& result) const; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/Socket.hh0000644000175000017500000000142112262345041017706 0ustar manuelmanuel00000000000000#ifndef SOCKET_HH #define SOCKET_HH #include #ifndef _WIN32 #include #include #include #include #include #include #else #include #endif namespace openmsx { #ifndef _WIN32 static const int OPENMSX_INVALID_SOCKET = -1; static const int SOCKET_ERROR = -1; typedef int SOCKET; #else // INVALID_SOCKET is #defined as (SOCKET)(~0) // but that gives a old-style-cast warning static const SOCKET OPENMSX_INVALID_SOCKET = static_cast(~0); #endif std::string sock_error(); void sock_startup(); void sock_cleanup(); void sock_close(SOCKET sd); int sock_recv(SOCKET sd, char* buf, size_t count); int sock_send(SOCKET sd, const char* buf, size_t count); } // namespace openmsx #endif openmsx-0.10.0/src/events/MSXCliComm.hh0000644000175000017500000000114612262345041020375 0ustar manuelmanuel00000000000000#ifndef MSXCLICOMM_HH #define MSXCLICOMM_HH #include "CliComm.hh" #include "StringMap.hh" #include "noncopyable.hh" namespace openmsx { class MSXMotherBoard; class GlobalCliComm; class MSXCliComm : public CliComm, private noncopyable { public: MSXCliComm(MSXMotherBoard& motherBoard, GlobalCliComm& cliComm); virtual void log(LogLevel level, string_ref message); virtual void update(UpdateType type, string_ref name, string_ref value); private: MSXMotherBoard& motherBoard; GlobalCliComm& cliComm; StringMap prevValues[NUM_UPDATES]; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/Socket.cc0000644000175000017500000001172012262345041017677 0ustar manuelmanuel00000000000000#include "Socket.hh" #include "MSXException.hh" #include #include #include namespace openmsx { std::string sock_error() { #ifdef _WIN32 static std::map errMap; static std::string UnknownError = "Unknown error"; static bool alreadyInit = false; if (!alreadyInit) { alreadyInit = true; errMap[0] = "No error"; errMap[WSAEINTR] = "Interrupted system call"; errMap[WSAEBADF] = "Bad file number"; errMap[WSAEACCES] = "Permission denied"; errMap[WSAEFAULT] = "Bad address"; errMap[WSAEINVAL] = "Invalid argument"; errMap[WSAEMFILE] = "Too many open sockets"; errMap[WSAEWOULDBLOCK] = "Operation would block"; errMap[WSAEINPROGRESS] = "Operation now in progress"; errMap[WSAEALREADY] = "Operation already in progress"; errMap[WSAENOTSOCK] = "Socket operation on non-socket"; errMap[WSAEDESTADDRREQ] = "Destination address required"; errMap[WSAEMSGSIZE] = "Message too long"; errMap[WSAEPROTOTYPE] = "Protocol wrong type for socket"; errMap[WSAENOPROTOOPT] = "Bad protocol option"; errMap[WSAEPROTONOSUPPORT] = "Protocol not supported"; errMap[WSAESOCKTNOSUPPORT] = "Socket type not supported"; errMap[WSAEOPNOTSUPP] = "Operation not supported on socket"; errMap[WSAEPFNOSUPPORT] = "Protocol family not supported"; errMap[WSAEAFNOSUPPORT] = "Address family not supported"; errMap[WSAEADDRINUSE] = "Address already in use"; errMap[WSAEADDRNOTAVAIL] = "Can't assign requested address"; errMap[WSAENETDOWN] = "Network is down"; errMap[WSAENETUNREACH] = "Network is unreachable"; errMap[WSAENETRESET] = "Net connection reset"; errMap[WSAECONNABORTED] = "Software caused connection abort"; errMap[WSAECONNRESET] = "Connection reset by peer"; errMap[WSAENOBUFS] = "No buffer space available"; errMap[WSAEISCONN] = "Socket is already connected"; errMap[WSAENOTCONN] = "Socket is not connected"; errMap[WSAESHUTDOWN] = "Can't send after socket shutdown"; errMap[WSAETOOMANYREFS] = "Too many references, can't splice"; errMap[WSAETIMEDOUT] = "Connection timed out"; errMap[WSAECONNREFUSED] = "Connection refused"; errMap[WSAELOOP] = "Too many levels of symbolic links"; errMap[WSAENAMETOOLONG] = "File name too long"; errMap[WSAEHOSTDOWN] = "Host is down"; errMap[WSAEHOSTUNREACH] = "No route to host"; errMap[WSAENOTEMPTY] = "Directory not empty"; errMap[WSAEPROCLIM] = "Too many processes"; errMap[WSAEUSERS] = "Too many users"; errMap[WSAEDQUOT] = "Disc quota exceeded"; errMap[WSAESTALE] = "Stale NFS file handle"; errMap[WSAEREMOTE] = "Too many levels of remote in path"; errMap[WSASYSNOTREADY] = "Network system is unavailable"; errMap[WSAVERNOTSUPPORTED] = "Winsock version out of range"; errMap[WSANOTINITIALISED] = "WSAStartup not yet called"; errMap[WSAEDISCON] = "Graceful shutdown in progress"; errMap[WSAHOST_NOT_FOUND] = "Host not found"; errMap[WSANO_DATA] = "No host data of that type was found"; } auto it = errMap.find(WSAGetLastError()); if (it != errMap.end()) { return it->second; } else { return UnknownError; } #else return strerror(errno); #endif } void sock_startup() { #ifdef _WIN32 // MAKEWORD is #define'd as ((WORD)(((BYTE)(a))|(((WORD)((BYTE)(b)))<<8))) // but using that gives old-style-cast warnings WORD w = 1 | (1 << 8); // MAKEWORD(1, 1) WSAData wsaData; if (WSAStartup(w, &wsaData) != 0) { throw FatalError(sock_error()); } #else // nothing needed for unix #endif } void sock_cleanup() { #ifdef _WIN32 WSACleanup(); #else // nothing needed for unix #endif } // close a connection void sock_close(SOCKET sd) { #ifdef _WIN32 closesocket(sd); #else close(sd); #endif } int sock_recv(SOCKET sd, char* buf, size_t count) { int num = recv(sd, buf, int(count), 0); if (num > 0) return num; // normal case if (num == 0) return -1; // socket was closed by client #ifdef _WIN32 // Something bad happened on the socket. It could just be a // "would block" notification, or it could be something more // serious. WSAEWOULDBLOCK can happen after select() says a // socket is readable under Win9x, it doesn't happen on // WinNT/2000 or on Unix. int err; int errlen = sizeof(err); getsockopt(sd, SOL_SOCKET, SO_ERROR, reinterpret_cast(&err), &errlen); if (err == WSAEWOULDBLOCK) return 0; return -1; #else if (errno == EWOULDBLOCK) return 0; return -1; #endif } int sock_send(SOCKET sd, const char* buf, size_t count) { int num = send(sd, buf, int(count), 0); if (num >= 0) return num; // normal case #ifdef _WIN32 int err; int errlen = sizeof(err); getsockopt(sd, SOL_SOCKET, SO_ERROR, reinterpret_cast(&err), &errlen); if (err == WSAEWOULDBLOCK) return 0; return -1; #else if (errno == EWOULDBLOCK) return 0; return -1; #endif } } // namespace openmsx openmsx-0.10.0/src/events/CliConnection.hh0000644000175000017500000000753512262345041021221 0ustar manuelmanuel00000000000000#ifndef CLICONNECTION_HH #define CLICONNECTION_HH #include "CliListener.hh" #include "Thread.hh" #include "Semaphore.hh" #include "EventListener.hh" #include "Socket.hh" #include "CliComm.hh" #include #include namespace openmsx { class CommandController; class EventDistributor; class CliConnection : public CliListener, private EventListener, protected Runnable { public: virtual ~CliConnection(); void setUpdateEnable(CliComm::UpdateType type, bool value); bool getUpdateEnable(CliComm::UpdateType type) const; protected: CliConnection(CommandController& commandController, EventDistributor& eventDistributor); virtual void output(string_ref message) = 0; /** Starts the helper thread. * Subclasses should call this method at the end of their constructor. * Subclasses should themself send the opening tag (startOutput()). */ void start(); /** End this connection by sending the closing tag * and then closing the stream. * Subclasses should call this method at the start of their destructor. */ void end(); /** Close the connection. After this method is called, calls to * output() should be ignored. */ virtual void close() = 0; /** Send opening XML tag, should be called exactly once by a subclass * shortly after opening a connection. Cannot be implemented in the * base class because some subclasses (want to send data before this * tag). */ void startOutput(); xmlParserCtxt* parser_context; Thread thread; // TODO: Possible to make this private? private: void execute(const std::string& command); // CliListener virtual void log(CliComm::LogLevel level, string_ref message); virtual void update(CliComm::UpdateType type, string_ref machine, string_ref name, string_ref value); // EventListener virtual int signalEvent(const std::shared_ptr& event); enum State { START, TAG_OPENMSX, TAG_COMMAND, END }; struct ParseState { State state; unsigned unknownLevel; std::string content; CliConnection* object; }; static void cb_start_element(void* user_data, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri, int nb_namespaces, const xmlChar** namespaces, int nb_attributes, int nb_defaulted, const xmlChar** attrs); static void cb_end_element(void* user_data, const xmlChar* localname, const xmlChar* prefix, const xmlChar* uri); static void cb_text(void* user_data, const xmlChar* chars, int len); xmlSAXHandler sax_handler; ParseState user_data; CommandController& commandController; EventDistributor& eventDistributor; bool updateEnabled[CliComm::NUM_UPDATES]; }; class StdioConnection : public CliConnection { public: StdioConnection(CommandController& commandController, EventDistributor& eventDistributor); virtual ~StdioConnection(); virtual void output(string_ref message); private: virtual void close(); virtual void run(); bool ok; }; #ifdef _WIN32 class PipeConnection : public CliConnection { public: PipeConnection(CommandController& commandController, EventDistributor& eventDistributor, string_ref name); virtual ~PipeConnection(); virtual void output(string_ref message); private: virtual void close(); virtual void run(); HANDLE pipeHandle; HANDLE shutdownEvent; }; #endif class SocketConnection : public CliConnection { public: SocketConnection(CommandController& commandController, EventDistributor& eventDistributor, SOCKET sd); virtual ~SocketConnection(); virtual void output(string_ref message); private: virtual void close(); virtual void run(); Semaphore sem; SOCKET sd; bool established; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/Event.hh0000644000175000017500000000700612262345041017544 0ustar manuelmanuel00000000000000#ifndef EVENT_HH #define EVENT_HH #include "noncopyable.hh" #include namespace openmsx { class TclObject; enum EventType { OPENMSX_KEY_UP_EVENT, OPENMSX_KEY_DOWN_EVENT, OPENMSX_MOUSE_MOTION_EVENT, OPENMSX_MOUSE_MOTION_GROUP_EVENT, OPENMSX_MOUSE_BUTTON_UP_EVENT, OPENMSX_MOUSE_BUTTON_DOWN_EVENT, OPENMSX_JOY_AXIS_MOTION_EVENT, OPENMSX_JOY_BUTTON_UP_EVENT, OPENMSX_JOY_BUTTON_DOWN_EVENT, OPENMSX_FOCUS_EVENT, OPENMSX_RESIZE_EVENT, OPENMSX_QUIT_EVENT, OPENMSX_OSD_CONTROL_RELEASE_EVENT, OPENMSX_OSD_CONTROL_PRESS_EVENT, OPENMSX_BOOT_EVENT, // sent when the MSX resets or power ups /** Sent when VDP (V99x8 or V9990) reaches the end of a frame */ OPENMSX_FINISH_FRAME_EVENT, /** Sent when a OPENMSX_FINISH_FRAME_EVENT caused a redraw of the screen. * So in other words send when a OPENMSX_FINISH_FRAME_EVENT event was send * and the frame was not skipped and the event came from the active video * source. */ OPENMSX_FRAME_DRAWN_EVENT, OPENMSX_BREAK_EVENT, OPENMSX_SWITCH_RENDERER_EVENT, /** Delayed repaint */ OPENMSX_DELAYED_REPAINT_EVENT, /** Throttle LED events */ OPENMSX_THROTTLE_LED_EVENT, /** Send when main-thread should save SRAM contents */ OPENMSX_SAVE_SRAM, /** Send when hotkey command should be repeated */ OPENMSX_REPEAT_HOTKEY, /** Used to schedule 'taking reverse snapshots' between Z80 instructions. */ OPENMSX_TAKE_REVERSE_SNAPSHOT, /** Command received on CliComm connection */ OPENMSX_CLICOMMAND_EVENT, /** Send when an after-emutime command should be executed. */ OPENMSX_AFTER_TIMED_EVENT, /** This event is periodically send (50 times per second atm). * Used to implement polling (e.g SDL input events). */ OPENMSX_POLL_EVENT, /** Send when a (new) machine configuration is loaded */ OPENMSX_MACHINE_LOADED_EVENT, /** Send when a machine is (de)activated. * This events is specific per machine. */ OPENMSX_MACHINE_ACTIVATED, OPENMSX_MACHINE_DEACTIVATED, /** Send when (part of) the openMSX window gets exposed, and thus * should be repainted. */ OPENMSX_EXPOSE_EVENT, /** Delete old MSXMotherboards */ OPENMSX_DELETE_BOARDS, OPENMSX_MIDI_IN_READER_EVENT, OPENMSX_MIDI_IN_WINDOWS_EVENT, OPENMSX_RS232_TESTER_EVENT, OPENMSX_AFTER_REALTIME_EVENT, OPENMSX_POINTER_TIMER_EVENT, NUM_EVENT_TYPES // must be last }; class Event : private noncopyable { public: virtual ~Event(); EventType getType() const; std::string toString() const; bool operator< (const Event& other) const; bool operator==(const Event& other) const; bool operator!=(const Event& other) const; /** Should 'bind -repeat' be stopped by 'other' event. * Normally all events should stop auto-repeat of the previous * event. But see OsdControlEvent for some exceptions. */ virtual bool isRepeatStopper(const Event& /*other*/) const { return true; } /** Does this event 'match' the given event. Normally an event * only matches itself (as defined by operator==). But e.g. * MouseMotionGroupEvent matches any MouseMotionEvent. */ virtual bool matches(const Event& other) const { return *this == other; } protected: explicit Event(EventType type); private: virtual void toStringImpl(TclObject& result) const = 0; virtual bool lessImpl(const Event& other) const = 0; const EventType type; }; // implementation for events that don't need additional data class SimpleEvent : public Event { public: SimpleEvent(EventType type) : Event(type) {} virtual void toStringImpl(TclObject& result) const; virtual bool lessImpl(const Event& other) const; }; } // namespace openmsx #endif openmsx-0.10.0/src/events/MessageCommand.cc0000644000175000017500000000256312262345041021337 0ustar manuelmanuel00000000000000#include "MessageCommand.hh" #include "CommandException.hh" #include "CliComm.hh" #include "xrange.hh" namespace openmsx { MessageCommand::MessageCommand(CommandController& controller) : Command(controller, "message") { } static CliComm::LogLevel getLevel(const std::string& level) { auto levels = CliComm::getLevelStrings(); for (auto i : xrange(levels.size())) { if (level == levels[i]) { return static_cast(i); } } throw CommandException("Unknown level string: " + level); } std::string MessageCommand::execute(const std::vector& tokens) { CliComm& cliComm = getCliComm(); CliComm::LogLevel level = CliComm::INFO; switch (tokens.size()) { case 3: level = getLevel(tokens[2]); // fall-through case 2: cliComm.log(level, tokens[1]); break; default: throw SyntaxError(); } return ""; } std::string MessageCommand::help(const std::vector& /*tokens*/) const { return "message []\n" "Print a message. (By default) this message will be shown in " "a colored box at the top of the screen. It's possible to " "specify a level for the message (e.g. 'info', 'warning' or " "'error')."; } void MessageCommand::tabCompletion(std::vector& tokens) const { if (tokens.size() == 3) { completeString(tokens, CliComm::getLevelStrings()); } } } // namespace openmsx openmsx-0.10.0/src/settings/0000755000175000017500000000000012262345041016473 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/settings/ProxySetting.hh0000644000175000017500000000164512262345041021501 0ustar manuelmanuel00000000000000#ifndef PROXYSETTING_HH #define PROXYSETTING_HH #include "Setting.hh" namespace openmsx { class Reactor; class ProxySetting : public BaseSetting { public: ProxySetting(Reactor& reactor, string_ref name); virtual void setString(const std::string& value); virtual string_ref getTypeString() const; virtual std::string getDescription() const; virtual std::string getString() const; virtual std::string getDefaultValue() const; virtual std::string getRestoreValue() const; virtual void setStringDirect(const std::string& value); virtual void tabCompletion(std::vector& tokens) const; virtual bool needLoadSave() const; virtual bool needTransfer() const; virtual void setDontSaveValue(const std::string& dontSaveValue); virtual void additionalInfo(TclObject& result) const; private: BaseSetting* getSetting(); const BaseSetting* getSetting() const; Reactor& reactor; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/EnumSetting.cc0000644000175000017500000000270412262345041021247 0ustar manuelmanuel00000000000000#include "EnumSetting.hh" #include "TclObject.hh" #include "Completer.hh" #include "CommandException.hh" #include "stringsp.hh" #include "unreachable.hh" #include "StringOp.hh" #include "stl.hh" #include namespace openmsx { typedef CmpTupleElement<0, StringOp::caseless> Comp; EnumSettingBase::EnumSettingBase(BaseMap&& map) : baseMap(std::move(map)) { sort(baseMap.begin(), baseMap.end(), Comp()); } int EnumSettingBase::fromStringBase(string_ref str) const { auto it = lower_bound(baseMap.begin(), baseMap.end(), str, Comp()); StringOp::casecmp cmp; if ((it == baseMap.end()) || !cmp(it->first, str)) { throw CommandException("not a valid value: " + str); } return it->second; } string_ref EnumSettingBase::toStringBase(int value) const { for (auto& p : baseMap) { if (p.second == value) { return p.first; } } UNREACHABLE; return ""; } std::vector EnumSettingBase::getPossibleValues() const { std::vector result; for (auto& p : baseMap) { result.push_back(p.first); } return result; } void EnumSettingBase::additionalInfoBase(TclObject& result) const { TclObject valueList(result.getInterpreter()); valueList.addListElements(getPossibleValues()); result.addListElement(valueList); } void EnumSettingBase::tabCompletionBase(std::vector& tokens) const { Completer::completeString(tokens, getPossibleValues(), false); // case insensitive } } // namespace openmsx openmsx-0.10.0/src/settings/BooleanSetting.hh0000644000175000017500000000122312262345041021727 0ustar manuelmanuel00000000000000#ifndef BOOLEANSETTING_HH #define BOOLEANSETTING_HH #include "Setting.hh" namespace openmsx { class BooleanSetting : public Setting { public: BooleanSetting(CommandController& commandController, string_ref name, string_ref description, bool initialValue, SaveSetting save = SAVE); virtual string_ref getTypeString() const; virtual void tabCompletion(std::vector& tokens) const; bool getBoolean() const { return getValue().getBoolean(); } void setBoolean(bool b) { setString(toString(b)); } private: static std::string toString(bool b) { return b ? "true" : "false"; } }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/node.mk0000644000175000017500000000044212262345041017751 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ SettingsManager \ Setting \ ProxySetting \ IntegerSetting \ FloatSetting \ StringSetting \ BooleanSetting \ EnumSetting \ FilenameSetting \ KeyCodeSetting \ VideoSourceSetting \ ReadOnlySetting \ UserSettings include build/node-end.mk openmsx-0.10.0/src/settings/VideoSourceSetting.hh0000644000175000017500000000206712262345041022606 0ustar manuelmanuel00000000000000#ifndef VIDEOSOURCESETTING_HH #define VIDEOSOURCESETTING_HH #include "Setting.hh" #include #include namespace openmsx { class VideoSourceSetting : public Setting { public: explicit VideoSourceSetting(CommandController& commandController); virtual string_ref getTypeString() const; virtual void additionalInfo(TclObject& result) const; virtual void tabCompletion(std::vector& tokens) const; int registerVideoSource(const std::string& source); void unregisterVideoSource(int source); int getSource(); void setSource(int id); private: std::vector getPossibleValues() const; void checkSetValue(string_ref value) const; bool has(int value) const; int has(string_ref value) const; typedef std::vector> Sources; Sources sources; }; class VideoSourceActivator { public: VideoSourceActivator( VideoSourceSetting& setting, const std::string& name); ~VideoSourceActivator(); int getID() const { return id; } private: VideoSourceSetting& setting; int id; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/StringSetting.hh0000644000175000017500000000061412262345041021621 0ustar manuelmanuel00000000000000#ifndef STRINGSETTING_HH #define STRINGSETTING_HH #include "Setting.hh" namespace openmsx { class StringSetting : public Setting { public: StringSetting(CommandController& commandController, string_ref name, string_ref description, string_ref initialValue, SaveSetting save = SAVE); virtual string_ref getTypeString() const; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/IntegerSetting.hh0000644000175000017500000000115512262345041021751 0ustar manuelmanuel00000000000000#ifndef INTEGERSETTING_HH #define INTEGERSETTING_HH #include "Setting.hh" namespace openmsx { /** A Setting with an integer value. */ class IntegerSetting : public Setting { public: IntegerSetting(CommandController& commandController, string_ref name, string_ref description, int initialValue, int minValue, int maxValue); virtual string_ref getTypeString() const; virtual void additionalInfo(TclObject& result) const; int getInt() const { return getValue().getInt(); } void setInt(int i); private: const int minValue; const int maxValue; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/ReadOnlySetting.cc0000644000175000017500000000127512262345041022062 0ustar manuelmanuel00000000000000#include "ReadOnlySetting.hh" #include "MSXException.hh" namespace openmsx { ReadOnlySetting::ReadOnlySetting( CommandController& commandController, string_ref name, string_ref description, const std::string& initialValue) : Setting(commandController, name, description, initialValue, Setting::DONT_TRANSFER) , roValue(initialValue) { setChecker([this](TclObject& newValue) { if (newValue.getString() != roValue) { throw MSXException("Read-only setting"); } }); } void ReadOnlySetting::setReadOnlyValue(const std::string& value) { roValue = value; setString(value); } string_ref ReadOnlySetting::getTypeString() const { return "read-only"; } } // namespace openmsx openmsx-0.10.0/src/settings/StringSetting.cc0000644000175000017500000000067412262345041021615 0ustar manuelmanuel00000000000000#include "StringSetting.hh" namespace openmsx { StringSetting::StringSetting(CommandController& commandController, string_ref name, string_ref description, string_ref initialValue, SaveSetting save) : Setting(commandController, name, description, initialValue.str(), save) { } string_ref StringSetting::getTypeString() const { return "string"; } } // namespace openmsx openmsx-0.10.0/src/settings/UserSettings.hh0000644000175000017500000000131612262345041021454 0ustar manuelmanuel00000000000000#ifndef USERSETTINGS_HH #define USERSETTINGS_HH #include "noncopyable.hh" #include "string_ref.hh" #include #include namespace openmsx { class CommandController; class UserSettingCommand; class Setting; class UserSettings : private noncopyable { public: typedef std::vector> Settings; explicit UserSettings(CommandController& commandController); ~UserSettings(); void addSetting(std::unique_ptr setting); void deleteSetting(Setting& setting); Setting* findSetting(string_ref name) const; const Settings& getSettings() const; private: const std::unique_ptr userSettingCommand; Settings settings; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/ReadOnlySetting.hh0000644000175000017500000000103512262345041022066 0ustar manuelmanuel00000000000000#ifndef READONLYSETTING_HH #define READONLYSETTING_HH #include "Setting.hh" namespace openmsx { class ReadOnlySetting : public Setting { public: ReadOnlySetting(CommandController& commandController, string_ref name, string_ref description, const std::string& initialValue); const TclObject& getValue() const { return Setting::getValue(); } void setReadOnlyValue(const std::string& value); virtual string_ref getTypeString() const; private: std::string roValue; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/VideoSourceSetting.cc0000644000175000017500000001021612262345041022567 0ustar manuelmanuel00000000000000#include "VideoSourceSetting.hh" #include "CommandException.hh" #include "Completer.hh" #include "StringOp.hh" namespace openmsx { VideoSourceSetting::VideoSourceSetting(CommandController& commandController) : Setting(commandController, "videosource", "selects the video source to display on the screen", "none", DONT_SAVE) { sources.push_back(std::make_pair("none", 0)); setChecker([this](TclObject& newValue) { checkSetValue(newValue.getString()); // may throw }); } void VideoSourceSetting::checkSetValue(string_ref value) const { // Special case: in case there are no videosources registered (yet), // the only allowed value is "none". In case there is at least one // registered source, this special value "none" should be hidden. if (((value == "none") && (sources.size() > 1)) || ((value != "none") && !has(value))) { throw CommandException("video source not available"); } } int VideoSourceSetting::getSource() { // Always try to find a better value than "none". string_ref str = getValue().getString(); if (str != "none") { // If current value is allowed, then keep it. if (int id = has(str)) { return id; } } // Search the best value from the current set of allowed values. int id = 0; if (!id) { id = has("Video9000"); } // in if (!id) { id = has("MSX"); } // order if (!id) { id = has("GFX9000"); } // of if (!id) { id = has("Laserdisc"); } // preference if (!id) { // This handles the "none" case, but also stuff like // multiple V99x8/V9990 chips. Prefer the source with // highest id (=newest). for (auto& p : sources) { id = std::max(id, p.second); } } setSource(id); // store new value return id; } void VideoSourceSetting::setSource(int id) { auto it = find_if(sources.begin(), sources.end(), [&](const Sources::value_type& p) { return p.second == id; }); assert(it != sources.end()); setString(it->first); } string_ref VideoSourceSetting::getTypeString() const { return "enumeration"; } std::vector VideoSourceSetting::getPossibleValues() const { std::vector result; if (sources.size() == 1) { assert(sources.front().first == "none"); result.push_back("none"); } else { for (auto& p : sources) { if (p.second != 0) { result.push_back(p.first); } } } return result; } void VideoSourceSetting::additionalInfo(TclObject& result) const { TclObject valueList(result.getInterpreter()); valueList.addListElements(getPossibleValues()); result.addListElement(valueList); } void VideoSourceSetting::tabCompletion(std::vector& tokens) const { Completer::completeString(tokens, getPossibleValues(), false); // case insensitive } int VideoSourceSetting::registerVideoSource(const std::string& source) { static int counter = 0; // id's are globally unique assert(!has(source)); sources.push_back(std::make_pair(source, ++counter)); // First announce extended set of allowed values before announcing a // (possibly) different value. notifyPropertyChange(); setSource(getSource()); // via source to (possibly) adjust value return counter; } void VideoSourceSetting::unregisterVideoSource(int source) { auto it = find_if(sources.begin(), sources.end(), [&](Sources::value_type& p) { return p.second == source; }); assert(it != sources.end()); sources.erase(it); // First notify the (possibly) changed value before announcing the // shrinked set of values. setSource(getSource()); // via source to (possibly) adjust value notifyPropertyChange(); } bool VideoSourceSetting::has(int value) const { for (auto& p : sources) { if (p.second == value) return true; } return false; } int VideoSourceSetting::has(string_ref value) const { auto it = find_if(sources.begin(), sources.end(), [&](const Sources::value_type& p) { StringOp::casecmp cmp; return cmp(p.first, value); }); return (it != sources.end()) ? it->second : 0; } VideoSourceActivator::VideoSourceActivator( VideoSourceSetting& setting_, const std::string& name) : setting(setting_) { id = setting.registerVideoSource(name); } VideoSourceActivator::~VideoSourceActivator() { setting.unregisterVideoSource(id); } } // namespace openmsx openmsx-0.10.0/src/settings/SettingsManager.cc0000644000175000017500000001374312262345041022105 0ustar manuelmanuel00000000000000#include "SettingsManager.hh" #include "GlobalCommandController.hh" #include "Command.hh" #include "InfoTopic.hh" #include "TclObject.hh" #include "Setting.hh" #include "CommandException.hh" #include "XMLElement.hh" #include "KeyRange.hh" #include "memory.hh" #include using std::string; using std::vector; namespace openmsx { // SettingsManager implementation: class SettingInfo : public InfoTopic { public: SettingInfo(InfoCommand& openMSXInfoCommand, SettingsManager& manager); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: SettingsManager& manager; }; class SetCompleter : public CommandCompleter { public: SetCompleter(CommandController& commandController, SettingsManager& manager); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: SettingsManager& manager; }; class SettingCompleter : public CommandCompleter { public: SettingCompleter(CommandController& commandController, SettingsManager& manager, const string& name); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: SettingsManager& manager; }; SettingsManager::SettingsManager(GlobalCommandController& commandController) : settingInfo(make_unique( commandController.getOpenMSXInfoCommand(), *this)) , setCompleter(make_unique( commandController, *this)) , incrCompleter(make_unique( commandController, *this, "incr")) , unsetCompleter(make_unique( commandController, *this, "unset")) { } SettingsManager::~SettingsManager() { assert(settingsMap.empty()); } void SettingsManager::registerSetting(BaseSetting& setting, string_ref name) { assert(settingsMap.find(name) == settingsMap.end()); settingsMap[name] = &setting; } void SettingsManager::unregisterSetting(BaseSetting& /*setting*/, string_ref name) { assert(settingsMap.find(name) != settingsMap.end()); settingsMap.erase(name); } BaseSetting* SettingsManager::findSetting(string_ref name) const { auto it = settingsMap.find(name); return (it != settingsMap.end()) ? it->second : nullptr; } // Helper functions for setting commands BaseSetting& SettingsManager::getByName(string_ref cmd, string_ref name) const { if (auto* setting = findSetting(name)) { return *setting; } throw CommandException(cmd + ": " + name + ": no such setting"); } void SettingsManager::loadSettings(const XMLElement& config) { // restore default values for (auto& p : settingsMap) { auto& setting = *p.second; if (setting.needLoadSave()) { setting.setString(setting.getRestoreValue()); } } // load new values auto* settings = config.findChild("settings"); if (!settings) return; for (auto& p : settingsMap) { auto name = p.first(); auto& setting = *p.second; if (!setting.needLoadSave()) continue; if (auto* elem = settings->findChildWithAttribute( "setting", "id", name)) { try { setting.setString(elem->getData()); } catch (MSXException&) { // ignore, keep default value } } } } // class SettingInfo SettingInfo::SettingInfo(InfoCommand& openMSXInfoCommand, SettingsManager& manager_) : InfoTopic(openMSXInfoCommand, "setting") , manager(manager_) { } void SettingInfo::execute( const vector& tokens, TclObject& result) const { auto& settingsMap = manager.settingsMap; switch (tokens.size()) { case 2: for (auto& p : settingsMap) { result.addListElement(p.first()); } break; case 3: { const auto& name = tokens[2].getString(); auto it = settingsMap.find(name); if (it == settingsMap.end()) { throw CommandException("No such setting: " + name); } it->second->info(result); break; } default: throw CommandException("Too many parameters."); } } string SettingInfo::help(const vector& /*tokens*/) const { return "openmsx_info setting : " "returns list of all settings\n" "openmsx_info setting : " "returns info on a specific setting\n"; } void SettingInfo::tabCompletion(vector& tokens) const { if (tokens.size() == 3) { // complete setting name completeString(tokens, keys(manager.settingsMap)); } } // class SetCompleter SetCompleter::SetCompleter(CommandController& commandController, SettingsManager& manager_) : CommandCompleter(commandController, "set"), manager(manager_) { } string SetCompleter::help(const vector& tokens) const { if (tokens.size() == 2) { return manager.getByName("set", tokens[1]).getDescription(); } return "Set or query the value of a openMSX setting or Tcl variable\n" " set shows current value\n" " set set a new value\n" "Use 'help set ' to get more info on a specific\n" "openMSX setting.\n"; } void SetCompleter::tabCompletion(vector& tokens) const { switch (tokens.size()) { case 2: // complete setting name completeString(tokens, keys(manager.settingsMap), false); // case insensitive break; case 3: { // complete setting value auto it = manager.settingsMap.find(tokens[1]); if (it != manager.settingsMap.end()) { it->second->tabCompletion(tokens); } break; } } } // class SettingCompleter SettingCompleter::SettingCompleter( CommandController& commandController, SettingsManager& manager_, const string& name) : CommandCompleter(commandController, name) , manager(manager_) { } string SettingCompleter::help(const vector& /*tokens*/) const { return ""; // TODO } void SettingCompleter::tabCompletion(vector& tokens) const { if (tokens.size() == 2) { // complete setting name completeString(tokens, keys(manager.settingsMap)); } } } // namespace openmsx openmsx-0.10.0/src/settings/SettingsManager.hh0000644000175000017500000000237212262345041022113 0ustar manuelmanuel00000000000000#ifndef SETTINGSMANAGER_HH #define SETTINGSMANAGER_HH #include "StringMap.hh" #include "string_ref.hh" #include "noncopyable.hh" #include #include namespace openmsx { class BaseSetting; class GlobalCommandController; class XMLElement; class SettingInfo; class SetCompleter; class SettingCompleter; /** Manages all settings. */ class SettingsManager : private noncopyable { public: explicit SettingsManager(GlobalCommandController& commandController); ~SettingsManager(); /** Find the setting with given name. * @return The requested setting or nullptr. */ BaseSetting* findSetting(string_ref name) const; void loadSettings(const XMLElement& config); void registerSetting (BaseSetting& setting, string_ref name); void unregisterSetting(BaseSetting& setting, string_ref name); private: BaseSetting& getByName(string_ref cmd, string_ref name) const; friend class SettingInfo; friend class SetCompleter; friend class SettingCompleter; const std::unique_ptr settingInfo; const std::unique_ptr setCompleter; const std::unique_ptr incrCompleter; const std::unique_ptr unsetCompleter; StringMap settingsMap; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/KeyCodeSetting.hh0000644000175000017500000000065712262345041021705 0ustar manuelmanuel00000000000000#ifndef KEYCODESETTING_HH #define KEYCODESETTING_HH #include "Setting.hh" #include "Keys.hh" namespace openmsx { class KeyCodeSetting : public Setting { public: KeyCodeSetting(CommandController& commandController, string_ref name, string_ref description, Keys::KeyCode initialValue); virtual string_ref getTypeString() const; Keys::KeyCode getKey() const; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/FilenameSetting.hh0000644000175000017500000000070412262345041022073 0ustar manuelmanuel00000000000000#ifndef FILENAMESETTING_HH #define FILENAMESETTING_HH #include "Setting.hh" namespace openmsx { class FilenameSetting : public Setting { public: FilenameSetting(CommandController& commandController, string_ref name, string_ref description, string_ref initialValue); virtual string_ref getTypeString() const; virtual void tabCompletion(std::vector& tokens) const; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/KeyCodeSetting.cc0000644000175000017500000000137212262345041021666 0ustar manuelmanuel00000000000000#include "KeyCodeSetting.hh" #include "CommandException.hh" namespace openmsx { KeyCodeSetting::KeyCodeSetting(CommandController& commandController, string_ref name, string_ref description, Keys::KeyCode initialValue) : Setting(commandController, name, description, Keys::getName(initialValue), SAVE) { setChecker([this](TclObject& newValue) { const auto& str = newValue.getString(); if (Keys::getCode(str) == Keys::K_NONE) { throw CommandException("Not a valid key: " + str); } }); } string_ref KeyCodeSetting::getTypeString() const { return "key"; } Keys::KeyCode KeyCodeSetting::getKey() const { return Keys::getCode(getValue().getString()); } } // namespace openmsx openmsx-0.10.0/src/settings/BooleanSetting.cc0000644000175000017500000000150512262345041021720 0ustar manuelmanuel00000000000000#include "BooleanSetting.hh" #include "Completer.hh" namespace openmsx { BooleanSetting::BooleanSetting( CommandController& commandController, string_ref name, string_ref description, bool initialValue, SaveSetting save) : Setting(commandController, name, description, toString(initialValue), save) { setChecker([this](TclObject& newValue) { // May throw. // Re-set the queried value to get a normalized value. newValue.setString(toString(newValue.getBoolean())); }); } string_ref BooleanSetting::getTypeString() const { return "boolean"; } void BooleanSetting::tabCompletion(std::vector& tokens) const { static const char* const values[] = { "true", "on", "yes", "false", "off", "no", }; Completer::completeString(tokens, values, false); // case insensitive } } // namespace openmsx openmsx-0.10.0/src/settings/FloatSetting.cc0000644000175000017500000000212112262345041021401 0ustar manuelmanuel00000000000000#include "FloatSetting.hh" namespace openmsx { static std::string toString(double d) { TclObject obj; obj.setDouble(d); return obj.getString().str(); } FloatSetting::FloatSetting(CommandController& commandController, string_ref name, string_ref description, double initialValue, double minValue_, double maxValue_) : Setting(commandController, name, description, toString(initialValue), SAVE) , minValue(minValue_) , maxValue(maxValue_) { setChecker([this](TclObject& newValue) { double value = newValue.getDouble(); // may throw double clipped = std::min(std::max(value, minValue), maxValue); newValue.setDouble(clipped); }); } string_ref FloatSetting::getTypeString() const { return "float"; } void FloatSetting::additionalInfo(TclObject& result) const { TclObject range(result.getInterpreter()); range.addListElement(minValue); range.addListElement(maxValue); result.addListElement(range); } void FloatSetting::setDouble (double d) { setString(toString(d)); } } // namespace openmsx openmsx-0.10.0/src/settings/EnumSetting.hh0000644000175000017500000000471112262345041021261 0ustar manuelmanuel00000000000000#ifndef ENUMSETTING_HH #define ENUMSETTING_HH #include "Setting.hh" #include #include #include namespace openmsx { class TclObject; // non-templatized base class class EnumSettingBase { protected: // cannot be string_ref because of the 'default_machine' setting typedef std::vector> BaseMap; EnumSettingBase(BaseMap&& m); int fromStringBase(string_ref str) const; string_ref toStringBase(int value) const; std::vector getPossibleValues() const; void additionalInfoBase(TclObject& result) const; void tabCompletionBase(std::vector& tokens) const; private: BaseMap baseMap; }; template class EnumSetting : private EnumSettingBase, public Setting { public: typedef std::vector> Map; EnumSetting(CommandController& commandController, string_ref name, string_ref description, T initialValue, Map& map_, SaveSetting save = SAVE); virtual string_ref getTypeString() const; virtual void additionalInfo(TclObject& result) const; virtual void tabCompletion(std::vector& tokens) const; T getEnum() const; void setEnum(T value); private: string_ref toString(T value) const; }; //------------- template EnumSetting::EnumSetting( CommandController& commandController, string_ref name, string_ref description, T initialValue, Map& map, SaveSetting save) : EnumSettingBase(BaseMap(std::make_move_iterator(map.begin()), std::make_move_iterator(map.end()))) , Setting(commandController, name, description, toString(initialValue).str(), save) { setChecker([this](TclObject& newValue) { fromStringBase(newValue.getString()); // may throw }); } template string_ref EnumSetting::getTypeString() const { return "enumeration"; } template void EnumSetting::additionalInfo(TclObject& result) const { additionalInfoBase(result); } template void EnumSetting::tabCompletion(std::vector& tokens) const { tabCompletionBase(tokens); } template T EnumSetting::getEnum() const { return static_cast(fromStringBase(getValue().getString())); } template void EnumSetting::setEnum(T value) { setString(toString(value).str()); } template string_ref EnumSetting::toString(T value) const { return toStringBase(static_cast(value)); } } // namespace openmsx #endif openmsx-0.10.0/src/settings/FilenameSetting.cc0000644000175000017500000000111012262345041022051 0ustar manuelmanuel00000000000000#include "FilenameSetting.hh" #include "Completer.hh" #include "FileContext.hh" namespace openmsx { FilenameSetting::FilenameSetting( CommandController& commandController, string_ref name, string_ref description, string_ref initialValue) : Setting(commandController, name, description, initialValue.str(), Setting::SAVE) { } string_ref FilenameSetting::getTypeString() const { return "filename"; } void FilenameSetting::tabCompletion(std::vector& tokens) const { Completer::completeFileName(tokens, SystemFileContext()); } } // namespace openmsx openmsx-0.10.0/src/settings/Setting.hh0000644000175000017500000001207312262345041020434 0ustar manuelmanuel00000000000000#ifndef SETTING_HH #define SETTING_HH #include "Subject.hh" #include "TclObject.hh" #include "noncopyable.hh" #include "string_ref.hh" #include #include namespace openmsx { class CommandController; class GlobalCommandController; class Interpreter; class XMLElement; class BaseSetting : private noncopyable { protected: BaseSetting(string_ref name); ~BaseSetting() {} public: /** Get the name of this setting. */ const std::string& getName() const; /** For SettingInfo */ void info(TclObject& result) const; /// pure virtual methods /// /** Get a description of this setting that can be presented to the user. */ virtual std::string getDescription() const = 0; /** Returns a string describing the setting type (integer, string, ..) * Could be used in a GUI to pick an appropriate setting widget. */ virtual string_ref getTypeString() const = 0; /** Helper method for info(). */ virtual void additionalInfo(TclObject& result) const = 0; /** Complete a partly typed value. * Default implementation does not complete anything, * subclasses can override this to complete according to their * specific value type. */ virtual void tabCompletion(std::vector& tokens) const = 0; /** Get the current value of this setting in a string format that can be * presented to the user. */ virtual std::string getString() const = 0; /** Get the default value of this setting. * This is the initial value of the setting. Default values don't * get saved in 'settings.xml'. */ virtual std::string getDefaultValue() const = 0; /** Get the value that will be set after a Tcl 'unset' command. * Usually this is the same as the default value. Though one * exception is 'renderer', see comments in RendererFactory.cc. */ virtual std::string getRestoreValue() const = 0; /** Change the value of this setting to the given value. * This method will trigger Tcl traces. * This value still passes via the 'checker-callback' (see below), * so the value may be adjusted. Or in case of an invalid value * this method may throw. */ virtual void setString(const std::string& value) = 0; /** Similar to setString(), but doesn't trigger Tcl traces. * Like setString(), the given value may be adjusted or rejected. * Should only be used by the Interpreter class. */ virtual void setStringDirect(const std::string& value) = 0; /** Does this setting need to be loaded or saved (settings.xml). */ virtual bool needLoadSave() const = 0; /** Does this setting need to be transfered on reverse. */ virtual bool needTransfer() const = 0; /** This value will never end up in the settings.xml file */ virtual void setDontSaveValue(const std::string& dontSaveValue) = 0; private: /** The name of this setting. */ const std::string name; }; class Setting : public BaseSetting, public Subject { public: enum SaveSetting { SAVE, // save, transfer DONT_SAVE, // no-save, transfer DONT_TRANSFER, // no-save, no-transfer }; virtual ~Setting(); /** Gets the current value of this setting as a TclObject. */ const TclObject& getValue() const { return value; } /** Set restore value. See getDefaultValue() and getRestoreValue(). */ void setRestoreValue(const std::string& value); /** Set value-check-callback. * The callback is called on each change of this settings value. * The callback has to posibility to * - change the value (modify the parameter) * - disallow the change (throw an exception) * The callback is only executed on each value change, even if the * new value is the same as the current value. However the callback * is not immediately executed once it's set (via this method). */ void setChecker(std::function checkFunc); // BaseSetting virtual void setString(const std::string& value); virtual std::string getDescription() const; virtual std::string getString() const; virtual std::string getDefaultValue() const; virtual std::string getRestoreValue() const; virtual void setStringDirect(const std::string& value); virtual void tabCompletion(std::vector& tokens) const; virtual bool needLoadSave() const; virtual void additionalInfo(TclObject& result) const; virtual bool needTransfer() const; virtual void setDontSaveValue(const std::string& dontSaveValue); // convenience functions CommandController& getCommandController() const; Interpreter& getInterpreter() const; protected: Setting(CommandController& commandController, string_ref name, string_ref description, const std::string& initialValue, SaveSetting save = SAVE); void notifyPropertyChange() const; private: GlobalCommandController& getGlobalCommandController() const; void notify() const; private: CommandController& commandController; const std::string description; std::function checkFunc; TclObject value; // TODO can we share the underlying Tcl var storage? const std::string defaultValue; std::string restoreValue; std::string dontSaveValue; const SaveSetting save; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/ProxySetting.cc0000644000175000017500000000510512262345041021462 0ustar manuelmanuel00000000000000#include "ProxySetting.hh" #include "GlobalCommandController.hh" #include "MSXCommandController.hh" #include "Reactor.hh" #include "MSXMotherBoard.hh" #include "MSXException.hh" using std::string; using std::vector; namespace openmsx { ProxySetting::ProxySetting(Reactor& reactor_, string_ref name) : BaseSetting(name) , reactor(reactor_) { } BaseSetting* ProxySetting::getSetting() { auto* motherBoard = reactor.getMotherBoard(); if (!motherBoard) return nullptr; return motherBoard->getMSXCommandController().findSetting(getName()); } const BaseSetting* ProxySetting::getSetting() const { return const_cast(this)->getSetting(); } void ProxySetting::setString(const std::string& value) { if (auto* setting = getSetting()) { setting->setString(value); } } string_ref ProxySetting::getTypeString() const { if (auto* setting = getSetting()) { return setting->getTypeString(); } else { return "proxy"; } } std::string ProxySetting::getDescription() const { if (auto* setting = getSetting()) { return setting->getDescription(); } else { return "proxy"; } } std::string ProxySetting::getString() const { if (auto* setting = getSetting()) { return setting->getString(); } else { throw MSXException("No setting '" + getName() + "' on current machine."); } } std::string ProxySetting::getDefaultValue() const { if (auto* setting = getSetting()) { return setting->getDefaultValue(); } else { return "proxy"; } } std::string ProxySetting::getRestoreValue() const { if (auto* setting = getSetting()) { return setting->getRestoreValue(); } else { return "proxy"; } } void ProxySetting::setStringDirect(const string& value) { if (auto* setting = getSetting()) { // note: not setStringDirect() setting->setString(value); } else { throw MSXException("No setting '" + getName() + "' on current machine."); } } void ProxySetting::tabCompletion(vector& tokens) const { if (auto* setting = getSetting()) { setting->tabCompletion(tokens); } } bool ProxySetting::needLoadSave() const { if (auto* setting = getSetting()) { return setting->needLoadSave(); } else { return false; } } bool ProxySetting::needTransfer() const { if (auto* setting = getSetting()) { return setting->needTransfer(); } else { return false; } } void ProxySetting::setDontSaveValue(const std::string& dontSaveValue) { if (auto* setting = getSetting()) { setting->setDontSaveValue(dontSaveValue); } } void ProxySetting::additionalInfo(TclObject& result) const { if (auto* setting = getSetting()) { setting->additionalInfo(result); } } } // namespace openmsx openmsx-0.10.0/src/settings/Setting.cc0000644000175000017500000001271312262345041020423 0ustar manuelmanuel00000000000000#include "Setting.hh" #include "Observer.hh" #include "CommandController.hh" #include "GlobalCommandController.hh" #include "MSXCommandController.hh" #include "SettingsConfig.hh" #include "TclObject.hh" #include "CliComm.hh" #include "XMLElement.hh" #include "MSXException.hh" #include "checked_cast.hh" #include #include using std::string; namespace openmsx { // class BaseSetting BaseSetting::BaseSetting(string_ref name_) : name (name_.str()) { } const string& BaseSetting::getName() const { return name; } void BaseSetting::info(TclObject& result) const { result.addListElement(getTypeString()); result.addListElement(getDefaultValue()); additionalInfo(result); } // class Setting Setting::Setting(CommandController& commandController_, string_ref name_, string_ref desc_, const string& initialValue, SaveSetting save_) : BaseSetting(name_) , commandController(commandController_) , description(desc_.str()) , value(TclObject(getInterpreter(), initialValue)) , defaultValue(initialValue) , restoreValue(initialValue) , save(save_) { checkFunc = [](TclObject&) { /* nothing */ }; if (needLoadSave()) { auto& settingsConfig = getGlobalCommandController() .getSettingsConfig().getXMLElement(); if (auto* config = settingsConfig.findChild("settings")) { if (auto* elem = config->findChildWithAttribute( "setting", "id", getName())) { try { setStringDirect(elem->getData()); } catch (MSXException&) { // saved value no longer valid, just keep default } } } } getCommandController().registerSetting(*this); // This is needed to for example inform catapult of the new setting // value when a setting was destroyed/recreated (by a machine switch // for example). notify(); } Setting::~Setting() { getCommandController().unregisterSetting(*this); } string Setting::getDescription() const { return description; } void Setting::setString(const string& value) { getCommandController().changeSetting(*this, value); } void Setting::notify() const { // Notify all subsystems of a change in the setting value. There // are actually quite a few subsystems involved with the settings: // - the setting classes themselves // - the Tcl variables (and possibly traces on those variables) // - Subject/Observers // - CliComm setting-change events (for external GUIs) // - SettingsConfig (keeps values, also of not yet created settings) // This method takes care of the last 3 in this list. Subject::notify(); commandController.getCliComm().update( CliComm::SETTING, getName(), getString()); // Always keep SettingsConfig in sync. auto& config = getGlobalCommandController().getSettingsConfig().getXMLElement(); auto& settings = config.getCreateChild("settings"); string value = getString(); if (!needLoadSave() || (value == getDefaultValue())) { // remove setting if (auto* elem = settings.findChildWithAttribute( "setting", "id", getName())) { settings.removeChild(*elem); } } else { // add (or overwrite) setting auto& elem = settings.getCreateChildWithAttribute( "setting", "id", getName()); // check for non-saveable value // (mechanism can be generalize later when needed) if (value == dontSaveValue) value = getRestoreValue(); elem.setData(value); } } void Setting::notifyPropertyChange() const { TclObject result; info(result); commandController.getCliComm().update( CliComm::SETTINGINFO, getName(), result.getString()); } bool Setting::needLoadSave() const { return save == SAVE; } bool Setting::needTransfer() const { return save != DONT_TRANSFER; } void Setting::setDontSaveValue(const std::string& dontSaveValue_) { dontSaveValue = dontSaveValue_; } CommandController& Setting::getCommandController() const { return commandController; } GlobalCommandController& Setting::getGlobalCommandController() const { if (auto* globalCommandController = dynamic_cast(&commandController)) { return *globalCommandController; } else { return checked_cast(&commandController) ->getGlobalCommandController(); } } Interpreter& Setting::getInterpreter() const { return commandController.getInterpreter(); } void Setting::tabCompletion(std::vector& /*tokens*/) const { // nothing } void Setting::additionalInfo(TclObject& /*result*/) const { // nothing } void Setting::setRestoreValue(const string& value) { restoreValue = value; } void Setting::setChecker(std::function checkFunc_) { checkFunc = checkFunc_; } string Setting::getString() const { return value.getString().str(); } string Setting::getDefaultValue() const { return defaultValue; } string Setting::getRestoreValue() const { return restoreValue; } void Setting::setStringDirect(const string& str) { TclObject newValue(getInterpreter(), str); checkFunc(newValue); if (newValue != value) { value = newValue; notify(); } // synchronize proxy auto* controller = dynamic_cast( &getCommandController()); if (!controller) { // This is not a machine specific setting. return; } if (!controller->isActive()) { // This setting does not belong to the active machine. return; } auto& globalController = controller->getGlobalCommandController(); // Tcl already makes sure this doesn't result in an endless loop. try { globalController.changeSetting(getName(), getString()); } catch (MSXException&) { // ignore } } } // namespace openmsx openmsx-0.10.0/src/settings/FloatSetting.hh0000644000175000017500000000120612262345041021416 0ustar manuelmanuel00000000000000#ifndef FLOATSETTING_HH #define FLOATSETTING_HH #include "Setting.hh" namespace openmsx { /** A Setting with a floating point value. */ class FloatSetting : public Setting { public: FloatSetting(CommandController& commandController, string_ref name, string_ref description, double initialValue, double minValue, double maxValue); virtual string_ref getTypeString() const; virtual void additionalInfo(TclObject& result) const; double getDouble() const { return getValue().getDouble(); } void setDouble (double d); private: const double minValue; const double maxValue; }; } // namespace openmsx #endif openmsx-0.10.0/src/settings/IntegerSetting.cc0000644000175000017500000000175612262345041021746 0ustar manuelmanuel00000000000000#include "IntegerSetting.hh" #include "StringOp.hh" namespace openmsx { IntegerSetting::IntegerSetting(CommandController& commandController, string_ref name, string_ref description, int initialValue, int minValue_, int maxValue_) : Setting(commandController, name, description, StringOp::toString(initialValue), SAVE) , minValue(minValue_) , maxValue(maxValue_) { setChecker([this](TclObject& newValue) { int value = newValue.getInt(); // may throw int clipped = std::min(std::max(value, minValue), maxValue); newValue.setInt(clipped); }); } string_ref IntegerSetting::getTypeString() const { return "integer"; } void IntegerSetting::additionalInfo(TclObject& result) const { TclObject range(result.getInterpreter()); range.addListElement(minValue); range.addListElement(maxValue); result.addListElement(range); } void IntegerSetting::setInt(int i) { setString(StringOp::toString(i)); } } // namespace openmsx openmsx-0.10.0/src/settings/UserSettings.cc0000644000175000017500000002140012262345041021436 0ustar manuelmanuel00000000000000#include "UserSettings.hh" #include "Command.hh" #include "CommandController.hh" #include "CommandException.hh" #include "TclObject.hh" #include "StringSetting.hh" #include "BooleanSetting.hh" #include "IntegerSetting.hh" #include "FloatSetting.hh" #include "memory.hh" #include using std::string; using std::vector; using std::unique_ptr; namespace openmsx { class UserSettingCommand : public Command { public: UserSettingCommand(UserSettings& userSettings, CommandController& commandController); virtual void execute(const vector& tokens, TclObject& result); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: void create (const vector& tokens, TclObject& result); void destroy(const vector& tokens, TclObject& result); void info (const vector& tokens, TclObject& result); unique_ptr createString (const vector& tokens); unique_ptr createBoolean(const vector& tokens); unique_ptr createInteger(const vector& tokens); unique_ptr createFloat (const vector& tokens); vector getSettingNames() const; UserSettings& userSettings; }; // class UserSettings UserSettings::UserSettings(CommandController& commandController_) : userSettingCommand(make_unique( *this, commandController_)) { } UserSettings::~UserSettings() { } void UserSettings::addSetting(unique_ptr setting) { assert(!findSetting(setting->getName())); settings.push_back(std::move(setting)); } void UserSettings::deleteSetting(Setting& setting) { auto it = find_if(settings.begin(), settings.end(), [&](unique_ptr& p) { return p.get() == &setting; }); assert(it != settings.end()); settings.erase(it); } Setting* UserSettings::findSetting(string_ref name) const { for (auto& s : settings) { if (s->getName() == name) { return s.get(); } } return nullptr; } const UserSettings::Settings& UserSettings::getSettings() const { return settings; } // class UserSettingCommand UserSettingCommand::UserSettingCommand(UserSettings& userSettings_, CommandController& commandController) : Command(commandController, "user_setting") , userSettings(userSettings_) { } void UserSettingCommand::execute(const vector& tokens, TclObject& result) { if (tokens.size() < 2) { throw SyntaxError(); } const auto& subCommand = tokens[1].getString(); if (subCommand == "create") { create(tokens, result); } else if (subCommand == "destroy") { destroy(tokens, result); } else if (subCommand == "info") { info(tokens, result); } else { throw CommandException( "Invalid subcommand '" + subCommand + "', expected " "'create', 'destroy' or 'info'."); } } void UserSettingCommand::create(const vector& tokens, TclObject& result) { if (tokens.size() < 5) { throw SyntaxError(); } const auto& type = tokens[2].getString(); const auto& name = tokens[3].getString(); if (getCommandController().findSetting(name)) { throw CommandException( "There already exists a setting with this name: " + name); } unique_ptr setting; if (type == "string") { setting = createString(tokens); } else if (type == "boolean") { setting = createBoolean(tokens); } else if (type == "integer") { setting = createInteger(tokens); } else if (type == "float") { setting = createFloat(tokens); } else { throw CommandException( "Invalid setting type '" + type + "', expected " "'string', 'boolean', 'integer' or 'float'."); } userSettings.addSetting(std::move(setting)); result.setString(tokens[3].getString()); // name } unique_ptr UserSettingCommand::createString(const vector& tokens) { if (tokens.size() != 6) { throw SyntaxError(); } const auto& name = tokens[3].getString(); const auto& desc = tokens[4].getString(); const auto& initVal = tokens[5].getString(); return make_unique( getCommandController(), name, desc, initVal); } unique_ptr UserSettingCommand::createBoolean(const vector& tokens) { if (tokens.size() != 6) { throw SyntaxError(); } const auto& name = tokens[3].getString(); const auto& desc = tokens[4].getString(); const auto& initVal = tokens[5].getBoolean(); return make_unique( getCommandController(), name, desc, initVal); } unique_ptr UserSettingCommand::createInteger(const vector& tokens) { if (tokens.size() != 8) { throw SyntaxError(); } const auto& name = tokens[3].getString(); const auto& desc = tokens[4].getString(); const auto& initVal = tokens[5].getInt(); const auto& minVal = tokens[6].getInt(); const auto& maxVal = tokens[7].getInt(); return make_unique( getCommandController(), name, desc, initVal, minVal, maxVal); } unique_ptr UserSettingCommand::createFloat(const vector& tokens) { if (tokens.size() != 8) { throw SyntaxError(); } const auto& name = tokens[3].getString(); const auto& desc = tokens[4].getString(); const auto& initVal = tokens[5].getDouble(); const auto& minVal = tokens[6].getDouble(); const auto& maxVal = tokens[7].getDouble(); return make_unique( getCommandController(), name, desc, initVal, minVal, maxVal); } void UserSettingCommand::destroy(const vector& tokens, TclObject& /*result*/) { if (tokens.size() != 3) { throw SyntaxError(); } const auto& name = tokens[2].getString(); auto* setting = userSettings.findSetting(name); if (!setting) { throw CommandException( "There is no user setting with this name: " + name); } userSettings.deleteSetting(*setting); } void UserSettingCommand::info(const vector& /*tokens*/, TclObject& result) { result.addListElements(getSettingNames()); } string UserSettingCommand::help(const vector& tokens) const { if (tokens.size() < 2) { return "Manage user-defined settings.\n" "\n" "User defined settings are mainly used in Tcl scripts " "to create variables (=settings) that are persistent over " "different openMSX sessions.\n" "\n" " user_setting create [ ]\n" " user_setting destroy \n" " user_setting info\n" "\n" "Use 'help user_setting ' to see more info " "on a specific subcommand."; } assert(tokens.size() >= 2); if (tokens[1] == "create") { return "user_setting create [ ]\n" "\n" "Create a user defined setting. The extra arguments have the following meaning:\n" " The type for the setting, must be 'string', 'boolean', 'integer' or 'float'.\n" " The name for the setting.\n" " A (short) description for this setting.\n" " This text can be queried via 'help set '.\n" " The initial value for the setting.\n" " This value is only used the very first time the setting is created, otherwise the value is taken from previous openMSX sessions.\n" " This parameter is only required for 'integer' and 'float' setting types.\n" " Together with max-value this parameter defines the range of valid values.\n" " See min-value."; } else if (tokens[1] == "destroy") { return "user_setting destroy \n" "\n" "Remove a previously defined user setting. This only " "removes the setting from the current openMSX session, " "the value of this setting is still preserved for " "future sessions."; } else if (tokens[1] == "info") { return "user_setting info\n" "\n" "Returns a list of all user defined settings that are " "active in this openMSX session."; } else { return "No such subcommand, see 'help user_setting'."; } } void UserSettingCommand::tabCompletion(vector& tokens) const { if (tokens.size() == 2) { static const char* const cmds[] = { "create", "destroy", "info" }; completeString(tokens, cmds); } else if ((tokens.size() == 3) && (tokens[1] == "create")) { static const char* const types[] = { "string", "boolean", "integer", "float" }; completeString(tokens, types); } else if ((tokens.size() == 3) && (tokens[1] == "destroy")) { completeString(tokens, getSettingNames()); } } vector UserSettingCommand::getSettingNames() const { vector result; for (auto& s : userSettings.getSettings()) { result.push_back(s->getName()); } return result; } } // namespace openmsx openmsx-0.10.0/src/config/0000755000175000017500000000000012262345041016100 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/config/node.mk0000644000175000017500000000030112262345041017350 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ XMLLoader XMLElement \ HardwareConfig \ SettingsConfig \ DeviceConfig \ HDR_ONLY:= \ ConfigException \ XMLException \ include build/node-end.mk openmsx-0.10.0/src/config/HardwareConfig.cc0000644000175000017500000002633112262345041021277 0ustar manuelmanuel00000000000000#include "HardwareConfig.hh" #include "XMLLoader.hh" #include "XMLException.hh" #include "DeviceConfig.hh" #include "XMLElement.hh" #include "LocalFileReference.hh" #include "FileContext.hh" #include "FileOperations.hh" #include "MSXMotherBoard.hh" #include "CartridgeSlotManager.hh" #include "MSXCPUInterface.hh" #include "DeviceFactory.hh" #include "CliComm.hh" #include "serialize.hh" #include "serialize_stl.hh" #include "StringOp.hh" #include "memory.hh" #include "unreachable.hh" #include "xrange.hh" #include #include using std::string; using std::vector; using std::unique_ptr; using std::move; namespace openmsx { unique_ptr HardwareConfig::createMachineConfig( MSXMotherBoard& motherBoard, const string& machineName) { auto result = make_unique(motherBoard, machineName); result->load("machines"); return result; } unique_ptr HardwareConfig::createExtensionConfig( MSXMotherBoard& motherBoard, const string& extensionName) { auto result = make_unique(motherBoard, extensionName); result->load("extensions"); result->setName(extensionName); return result; } unique_ptr HardwareConfig::createRomConfig( MSXMotherBoard& motherBoard, const string& romfile, const string& slotname, const vector& options) { auto result = make_unique(motherBoard, "rom"); const auto& sramfile = FileOperations::getFilename(romfile); auto context = make_unique("roms/" + sramfile); vector ipsfiles; string mapper; bool romTypeOptionFound = false; // parse options for (auto it = options.begin(); it != options.end(); ++it) { const auto& option = *it++; if (it == options.end()) { throw MSXException("Missing argument for option \"" + option + '\"'); } if (option == "-ips") { if (!FileOperations::isRegularFile(context->resolve(*it))) { throw MSXException("Invalid IPS file: " + *it); } ipsfiles.push_back(*it); } else if (option == "-romtype") { if (!romTypeOptionFound) { mapper = *it; romTypeOptionFound = true; } else { throw MSXException("Only one -romtype option is allowed"); } } else { throw MSXException("Invalid option \"" + option + '\"'); } } string resolvedFilename = FileOperations::getAbsolutePath( context->resolve(romfile)); if (!FileOperations::isRegularFile(resolvedFilename)) { throw MSXException("Invalid ROM file: " + resolvedFilename); } XMLElement extension("extension"); XMLElement devices("devices"); XMLElement primary("primary"); primary.addAttribute("slot", slotname); XMLElement secondary("secondary"); secondary.addAttribute("slot", slotname); XMLElement device("ROM"); device.addAttribute("id", "MSXRom"); XMLElement mem("mem"); mem.addAttribute("base", "0x0000"); mem.addAttribute("size", "0x10000"); device.addChild(move(mem)); XMLElement rom("rom"); rom.addChild(XMLElement("resolvedFilename", resolvedFilename)); rom.addChild(XMLElement("filename", romfile)); if (!ipsfiles.empty()) { XMLElement patches("patches"); for (auto& s : ipsfiles) { patches.addChild(XMLElement("ips", s)); } rom.addChild(move(patches)); } device.addChild(move(rom)); XMLElement sound("sound"); sound.addChild(XMLElement("volume", "9000")); device.addChild(move(sound)); device.addChild(XMLElement( "mappertype", mapper.empty() ? "auto" : mapper)); device.addChild(XMLElement("sramname", sramfile + ".SRAM")); secondary.addChild(move(device)); primary.addChild(move(secondary)); devices.addChild(move(primary)); extension.addChild(move(devices)); result->setConfig(move(extension)); result->setName(romfile); result->setFileContext(move(context)); return result; } HardwareConfig::HardwareConfig(MSXMotherBoard& motherBoard_, const string& hwName_) : motherBoard(motherBoard_) , hwName(hwName_) { for (auto ps : xrange(4)) { for (auto ss : xrange(4)) { externalSlots[ps][ss] = false; } externalPrimSlots[ps] = false; expandedSlots[ps] = false; allocatedPrimarySlots[ps] = false; } userName = motherBoard.getUserName(hwName); } HardwareConfig::~HardwareConfig() { motherBoard.freeUserName(hwName, userName); #ifndef NDEBUG try { testRemove(); } catch (MSXException& e) { std::cerr << e.getMessage() << std::endl; UNREACHABLE; } #endif while (!devices.empty()) { motherBoard.removeDevice(*devices.back()); devices.pop_back(); } auto& slotManager = motherBoard.getSlotManager(); for (auto ps : xrange(4)) { for (auto ss : xrange(4)) { if (externalSlots[ps][ss]) { slotManager.removeExternalSlot(ps, ss); } } if (externalPrimSlots[ps]) { slotManager.removeExternalSlot(ps); } if (expandedSlots[ps]) { motherBoard.getCPUInterface().unsetExpanded(ps); } if (allocatedPrimarySlots[ps]) { slotManager.freePrimarySlot(ps, *this); } } } void HardwareConfig::testRemove() const { std::vector alreadyRemoved; for (auto it = devices.rbegin(); it != devices.rend(); ++it) { (*it)->testRemove(alreadyRemoved); alreadyRemoved.push_back(it->get()); } auto& slotManager = motherBoard.getSlotManager(); for (auto ps : xrange(4)) { for (auto ss : xrange(4)) { if (externalSlots[ps][ss]) { slotManager.testRemoveExternalSlot(ps, ss, *this); } } if (externalPrimSlots[ps]) { slotManager.testRemoveExternalSlot(ps, *this); } if (expandedSlots[ps]) { motherBoard.getCPUInterface().testUnsetExpanded( ps, alreadyRemoved); } } } const FileContext& HardwareConfig::getFileContext() const { return *context; } void HardwareConfig::setFileContext(unique_ptr context_) { context = move(context_); } const XMLElement& HardwareConfig::getDevices() const { return getConfig().getChild("devices"); } XMLElement HardwareConfig::loadConfig(string_ref type, string_ref name) { return loadConfig(getFilename(type, name)); } XMLElement HardwareConfig::loadConfig(const string& filename) { try { LocalFileReference fileRef(filename); return XMLLoader::load(fileRef.getFilename(), "msxconfig2.dtd"); } catch (XMLException& e) { throw MSXException( "Loading of hardware configuration failed: " + e.getMessage()); } } string HardwareConfig::getFilename(string_ref type, string_ref name) { SystemFileContext context; try { // try .xml return context.resolve(FileOperations::join( type, name + ".xml")); } catch (MSXException& e) { // backwards-compatibility: // also try /hardwareconfig.xml try { return context.resolve(FileOperations::join( type, name, "hardwareconfig.xml")); } catch (MSXException&) { throw e; // signal first error } } } void HardwareConfig::load(string_ref type) { string filename = getFilename(type, hwName); setConfig(loadConfig(filename)); assert(!userName.empty()); const auto& baseName = FileOperations::getBaseName(filename); setFileContext(make_unique( baseName, hwName, userName)); } void HardwareConfig::parseSlots() { // TODO this code does parsing for both 'expanded' and 'external' slots // once machine and extensions are parsed separately move parsing // of 'expanded' to MSXCPUInterface // for (auto& psElem : getDevices().getChildren("primary")) { const auto& primSlot = psElem->getAttribute("slot"); int ps = CartridgeSlotManager::getSlotNum(primSlot); if (psElem->getAttributeAsBool("external", false)) { if (ps < 0) { throw MSXException( "Cannot mark unspecified primary slot '" + primSlot + "' as external"); } createExternalSlot(ps); continue; } for (auto& ssElem : psElem->getChildren("secondary")) { const auto& secSlot = ssElem->getAttribute("slot"); int ss = CartridgeSlotManager::getSlotNum(secSlot); if (ss < 0) { if ((ss >= -128) && (0 <= ps) && (ps < 4) && motherBoard.getCPUInterface().isExpanded(ps)) { ss += 128; } else { continue; } } if (ps < 0) { ps = getFreePrimarySlot(); auto mutableElem = const_cast(psElem); mutableElem->setAttribute("slot", StringOp::toString(ps)); } createExpandedSlot(ps); if (ssElem->getAttributeAsBool("external", false)) { createExternalSlot(ps, ss); } } } } void HardwareConfig::createDevices() { createDevices(getDevices(), nullptr, nullptr); } void HardwareConfig::createDevices(const XMLElement& elem, const XMLElement* primary, const XMLElement* secondary) { for (auto& c : elem.getChildren()) { const auto& name = c.getName(); if (name == "primary") { createDevices(c, &c, secondary); } else if (name == "secondary") { createDevices(c, primary, &c); } else { auto device = DeviceFactory::create( DeviceConfig(*this, c, primary, secondary)); if (device) { addDevice(move(device)); } else { motherBoard.getMSXCliComm().printWarning( "Deprecated device: \"" + name + "\", please upgrade your " "hardware descriptions."); } } } } void HardwareConfig::createExternalSlot(int ps) { motherBoard.getSlotManager().createExternalSlot(ps); assert(!externalPrimSlots[ps]); externalPrimSlots[ps] = true; } void HardwareConfig::createExternalSlot(int ps, int ss) { motherBoard.getSlotManager().createExternalSlot(ps, ss); assert(!externalSlots[ps][ss]); externalSlots[ps][ss] = true; } void HardwareConfig::createExpandedSlot(int ps) { if (!expandedSlots[ps]) { motherBoard.getCPUInterface().setExpanded(ps); expandedSlots[ps] = true; } } int HardwareConfig::getFreePrimarySlot() { int ps; motherBoard.getSlotManager().allocatePrimarySlot(ps, *this); assert(!allocatedPrimarySlots[ps]); allocatedPrimarySlots[ps] = true; return ps; } void HardwareConfig::addDevice(std::unique_ptr device) { motherBoard.addDevice(*device); devices.push_back(move(device)); } const string& HardwareConfig::getName() const { return name; } void HardwareConfig::setName(const string& proposedName) { if (!motherBoard.findExtension(proposedName)) { name = proposedName; } else { unsigned n = 0; do { name = StringOp::Builder() << proposedName << " (" << ++n << ')'; } while (motherBoard.findExtension(name)); } } // version 1: initial version // version 2: moved FileContext here (was part of config) // version 3: hold 'config' by-value instead of by-pointer template void HardwareConfig::serialize(Archive& ar, unsigned version) { // filled-in by constructor: // motherBoard, hwName, userName // filled-in by parseSlots() // externalSlots, externalPrimSlots, expandedSlots, allocatedPrimarySlots if (ar.versionBelow(version, 2)) { XMLElement::getLastSerializedFileContext(); // clear any previous value } ar.serialize("config", config); // fills in getLastSerializedFileContext() if (ar.versionAtLeast(version, 2)) { ar.serialize("context", context); } else { context = XMLElement::getLastSerializedFileContext(); assert(context); } if (ar.isLoader()) { if (!motherBoard.getMachineConfig()) { // must be done before parseSlots() motherBoard.setMachineConfig(this); } else { // already set because this is an extension } parseSlots(); createDevices(); } // only (polymorphically) initialize devices, they are already created for (auto& d : devices) { ar.serializePolymorphic("device", *d); } ar.serialize("name", name); } INSTANTIATE_SERIALIZE_METHODS(HardwareConfig); } // namespace openmsx openmsx-0.10.0/src/config/HardwareConfig.hh0000644000175000017500000000560312262345041021310 0ustar manuelmanuel00000000000000#ifndef HARDWARECONFIG_HH #define HARDWARECONFIG_HH #include "XMLElement.hh" #include "serialize_meta.hh" #include "serialize_constr.hh" #include "noncopyable.hh" #include #include #include namespace openmsx { class MSXMotherBoard; class MSXDevice; class FileContext; class HardwareConfig : private noncopyable { public: static XMLElement loadConfig(string_ref type, string_ref name); static std::unique_ptr createMachineConfig( MSXMotherBoard& motherBoard, const std::string& machineName); static std::unique_ptr createExtensionConfig( MSXMotherBoard& motherBoard, const std::string& extensionName); static std::unique_ptr createRomConfig( MSXMotherBoard& motherBoard, const std::string& romfile, const std::string& slotname, const std::vector& options); HardwareConfig(MSXMotherBoard& motherBoard, const std::string& hwName); ~HardwareConfig(); MSXMotherBoard& getMotherBoard() const { return motherBoard; } const FileContext& getFileContext() const; void setFileContext(std::unique_ptr context); const XMLElement& getConfig() const { return config; } const std::string& getName() const; void parseSlots(); void createDevices(); /** Checks whether this HardwareConfig can be deleted. * Throws an exception if not. */ void testRemove() const; template void serialize(Archive& ar, unsigned version); private: static std::string getFilename(string_ref type, string_ref name); static XMLElement loadConfig(const std::string& filename); void setConfig(XMLElement config_) { config = std::move(config_); } void load(string_ref type); const XMLElement& getDevices() const; void createDevices(const XMLElement& elem, const XMLElement* primary, const XMLElement* secondary); void createExternalSlot(int ps); void createExternalSlot(int ps, int ss); void createExpandedSlot(int ps); int getFreePrimarySlot(); void addDevice(std::unique_ptr device); void setName(const std::string& proposedName); MSXMotherBoard& motherBoard; std::string hwName; std::string userName; XMLElement config; std::unique_ptr context; bool externalSlots[4][4]; bool externalPrimSlots[4]; bool expandedSlots[4]; bool allocatedPrimarySlots[4]; std::vector> devices; std::string name; friend struct SerializeConstructorArgs; }; SERIALIZE_CLASS_VERSION(HardwareConfig, 3); template<> struct SerializeConstructorArgs { typedef std::tuple type; template void save( Archive& ar, const HardwareConfig& config) { ar.serialize("hwname", config.hwName); } template type load(Archive& ar, unsigned /*version*/) { std::string name; ar.serialize("hwname", name); return std::make_tuple(name); } }; } // namespace openmsx #endif openmsx-0.10.0/src/config/XMLElement.hh0000644000175000017500000000723112262345041020376 0ustar manuelmanuel00000000000000#ifndef XMLELEMENT_HH #define XMLELEMENT_HH #include "serialize_constr.hh" #include "serialize_meta.hh" #include #include #include #include namespace StringOp { class Builder; } namespace openmsx { class FileContext; class XMLElement { public: // // Basic functions // // Construction. // (copy, assign, move, destruct are default) XMLElement() {} explicit XMLElement(string_ref name); XMLElement(string_ref name, string_ref data); // name const std::string& getName() const { return name; } void setName(string_ref name); void clearName(); // data const std::string& getData() const { return data; } void setData(string_ref data); // attribute void addAttribute(string_ref name, string_ref value); void setAttribute(string_ref name, string_ref value); void removeAttribute(string_ref name); // child typedef std::vector Children; XMLElement& addChild(XMLElement child); void removeChild(const XMLElement& child); const Children& getChildren() const { return children; } bool hasChildren() const { return !children.empty(); } // // Convenience functions // // data bool getDataAsBool() const; int getDataAsInt() const; double getDataAsDouble() const; // attribute bool hasAttribute(string_ref name) const; const std::string& getAttribute(string_ref attName) const; string_ref getAttribute(string_ref attName, string_ref defaultValue) const; bool getAttributeAsBool(string_ref attName, bool defaultValue = false) const; int getAttributeAsInt(string_ref attName, int defaultValue = 0) const; bool findAttributeInt(string_ref attName, unsigned& result) const; // child const XMLElement* findChild(string_ref name) const; XMLElement* findChild(string_ref name); const XMLElement& getChild(string_ref name) const; XMLElement& getChild(string_ref name); const XMLElement* findChildWithAttribute( string_ref name, string_ref attName, string_ref attValue) const; XMLElement* findChildWithAttribute( string_ref name, string_ref attName, string_ref attValue); const XMLElement* findNextChild(string_ref name, size_t& fromIndex) const; std::vector getChildren(string_ref name) const; XMLElement& getCreateChild(string_ref name, string_ref defaultValue = ""); XMLElement& getCreateChildWithAttribute( string_ref name, string_ref attName, string_ref attValue); const std::string& getChildData(string_ref name) const; string_ref getChildData(string_ref name, string_ref defaultValue) const; bool getChildDataAsBool(string_ref name, bool defaultValue = false) const; int getChildDataAsInt(string_ref name, int defaultValue = 0) const; void setChildData(string_ref name, string_ref value); void removeAllChildren(); // various std::string dump() const; static std::string XMLEscape(const std::string& str); template void serialize(Archive& ar, unsigned version); // For backwards compatibility with older savestates static std::unique_ptr getLastSerializedFileContext(); private: typedef std::pair Attribute; typedef std::vector Attributes; Attributes::iterator findAttribute(string_ref name); Attributes::const_iterator findAttribute(string_ref name) const; void dump(StringOp::Builder& result, unsigned indentNum) const; std::string name; std::string data; Children children; Attributes attributes; }; SERIALIZE_CLASS_VERSION(XMLElement, 2); } // namespace openmsx #endif openmsx-0.10.0/src/config/SettingsConfig.hh0000644000175000017500000000225712262345041021355 0ustar manuelmanuel00000000000000#ifndef SETTINGSCONFIG_HH #define SETTINGSCONFIG_HH #include "XMLElement.hh" #include "noncopyable.hh" #include #include namespace openmsx { class SettingsManager; class FileContext; class HotKey; class GlobalCommandController; class CommandController; class SaveSettingsCommand; class LoadSettingsCommand; class SettingsConfig : private noncopyable { public: SettingsConfig(GlobalCommandController& globalCommandController, HotKey& hotKey); ~SettingsConfig(); void loadSetting(const FileContext& context, const std::string& filename); void saveSetting(const std::string& filename = ""); void setSaveSettings(bool save); void setSaveFilename(const FileContext& context, const std::string& filename); SettingsManager& getSettingsManager(); XMLElement& getXMLElement() { return xmlElement; } private: CommandController& commandController; const std::unique_ptr saveSettingsCommand; const std::unique_ptr loadSettingsCommand; const std::unique_ptr settingsManager; XMLElement xmlElement; HotKey& hotKey; std::string saveName; bool mustSaveSettings; }; } // namespace openmsx #endif openmsx-0.10.0/src/config/DeviceConfig.hh0000644000175000017500000000476412262345041020761 0ustar manuelmanuel00000000000000#ifndef DEVICECONFIG_HH #define DEVICECONFIG_HH #include "string_ref.hh" #include namespace openmsx { class XMLElement; class HardwareConfig; class FileContext; class MSXMotherBoard; class CliComm; class CommandController; class Scheduler; class Reactor; class GlobalSettings; class DeviceConfig { public: DeviceConfig() : hwConf(nullptr), devConf(nullptr) , primary(nullptr), secondary(nullptr) { } DeviceConfig(const HardwareConfig& hwConf_, const XMLElement& devConf_) : hwConf(&hwConf_), devConf(&devConf_) , primary(nullptr), secondary(nullptr) { } DeviceConfig(const HardwareConfig& hwConf_, const XMLElement& devConf_, const XMLElement* primary_, const XMLElement* secondary_) : hwConf(&hwConf_), devConf(&devConf_) , primary(primary_), secondary(secondary_) { } DeviceConfig(const DeviceConfig& other, const XMLElement& devConf_) : hwConf(other.hwConf), devConf(&devConf_) , primary(nullptr), secondary(nullptr) { } DeviceConfig(const DeviceConfig& other, const XMLElement* devConf_) : hwConf(other.hwConf), devConf(devConf_) , primary(nullptr), secondary(nullptr) { } const HardwareConfig& getHardwareConfig() const { assert(hwConf); return *hwConf; } const XMLElement* getXML() const { return devConf; } XMLElement* getPrimary() const { return const_cast(primary); } XMLElement* getSecondary() const { return const_cast(secondary); } // convenience methods: // methods below simply delegate to HardwareConfig or XMLElement const FileContext& getFileContext() const; MSXMotherBoard& getMotherBoard() const; CliComm& getCliComm() const; CommandController& getCommandController() const; Scheduler& getScheduler() const; Reactor& getReactor() const; GlobalSettings& getGlobalSettings() const; const XMLElement& getChild(string_ref name) const; const std::string& getChildData(string_ref name) const; string_ref getChildData(string_ref name, string_ref defaultValue) const; int getChildDataAsInt(string_ref name, int defaultValue = 0) const; bool getChildDataAsBool(string_ref name, bool defaultValue = false) const; const XMLElement* findChild(string_ref name) const; const std::string& getAttribute(string_ref attName) const; int getAttributeAsInt(string_ref attName, int defaultValue = 0) const; private: const HardwareConfig* hwConf; const XMLElement* devConf; const XMLElement* primary; const XMLElement* secondary; }; } // namespace openmsx #endif openmsx-0.10.0/src/config/XMLLoader.cc0000644000175000017500000001040512262345041020176 0ustar manuelmanuel00000000000000#include "XMLLoader.hh" #include "XMLElement.hh" #include "XMLException.hh" #include "File.hh" #include "FileException.hh" #include "memory.hh" #include "xrange.hh" #include #include #include #include #include using std::unique_ptr; using std::vector; using std::string; namespace openmsx { namespace XMLLoader { struct XMLLoaderHelper { XMLLoaderHelper() { } XMLElement root; vector current; string data; string systemID; }; static void cbStartElement( void* helper_, const xmlChar* localname, const xmlChar* /*prefix*/, const xmlChar* /*uri*/, int /*nb_namespaces*/, const xmlChar** /*namespaces*/, int nb_attributes, int /*nb_defaulted*/, const xmlChar** attrs) { auto* helper = static_cast(helper_); XMLElement newElem(reinterpret_cast(localname)); for (auto i : xrange(nb_attributes)) { auto valueStart = reinterpret_cast(attrs[i * 5 + 3]); auto valueEnd = reinterpret_cast(attrs[i * 5 + 4]); newElem.addAttribute( reinterpret_cast(attrs[i * 5 + 0]), std::string(valueStart, valueEnd - valueStart)); } XMLElement* newElem2; if (!helper->current.empty()) { newElem2 = &helper->current.back()->addChild(std::move(newElem)); } else { helper->root = std::move(newElem); newElem2 = &helper->root; } helper->current.push_back(newElem2); helper->data.clear(); } static void cbEndElement( void* helper_, const xmlChar* localname, const xmlChar* /*prefix*/, const xmlChar* /*uri*/) { auto* helper = static_cast(helper_); assert(!helper->current.empty()); auto& current = *helper->current.back(); assert(reinterpret_cast(localname) == current.getName()); (void)localname; if (!current.hasChildren()) { current.setData(helper->data); } helper->current.pop_back(); } static void cbCharacters(void* helper_, const xmlChar* chars, int len) { auto* helper = static_cast(helper_); assert(!helper->current.empty()); if (!helper->current.back()->hasChildren()) { helper->data.append(reinterpret_cast(chars), len); } } static void cbInternalSubset(void* helper_, const xmlChar* /*name*/, const xmlChar* /*extID*/, const xmlChar* systemID) { auto* helper = static_cast(helper_); helper->systemID = reinterpret_cast(systemID); } XMLElement load(const string& filename, const string& systemID) { File file(filename); // TODO: Reading blocks to a fixed-size buffer would require less memory // when reading (g)zipped XML. // Note: On destruction of "file", munmap() is called automatically. const byte* fileContent; size_t size_; try { fileContent = file.mmap(size_); } catch (FileException& e) { throw XMLException(filename + ": failed to mmap: " + e.getMessage()); } if (size_ > size_t(std::numeric_limits::max())) { throw XMLException(filename + ": file too big"); } auto size = int(size_); xmlSAXHandler handler; memset(&handler, 0, sizeof(handler)); handler.startElementNs = cbStartElement; handler.endElementNs = cbEndElement; handler.characters = cbCharacters; handler.internalSubset = cbInternalSubset; handler.initialized = XML_SAX2_MAGIC; XMLLoaderHelper helper; xmlParserCtxtPtr ctxt = xmlCreatePushParserCtxt( &handler, &helper, nullptr, 0, filename.c_str() ); if (!ctxt) { throw XMLException(filename + ": Could not create XML parser context"); } const int parseError = xmlParseChunk( ctxt, reinterpret_cast(fileContent), size, true); xmlFreeParserCtxt(ctxt); if (parseError) { throw XMLException(filename + ": Document parsing failed"); } if (helper.root.getName().empty()) { throw XMLException(filename + ": Document doesn't contain mandatory root Element"); } if (helper.systemID.empty()) { throw XMLException(filename + ": Missing systemID.\n" "You're probably using an old incompatible file format."); } if (helper.systemID != systemID) { throw XMLException(filename + ": systemID doesn't match " "(expected " + systemID + ", got " + helper.systemID + ")\n" "You're probably using an old incompatible file format."); } return std::move(helper.root); } } // namespace XMLLoader } // namespace openmsx openmsx-0.10.0/src/config/DeviceConfig.cc0000644000175000017500000000363212262345041020740 0ustar manuelmanuel00000000000000#include "DeviceConfig.hh" #include "XMLElement.hh" #include "HardwareConfig.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" namespace openmsx { const FileContext& DeviceConfig::getFileContext() const { return getHardwareConfig().getFileContext(); } MSXMotherBoard& DeviceConfig::getMotherBoard() const { return getHardwareConfig().getMotherBoard(); } CliComm& DeviceConfig::getCliComm() const { return getMotherBoard().getMSXCliComm(); } CommandController& DeviceConfig::getCommandController() const { return getMotherBoard().getCommandController(); } Scheduler& DeviceConfig::getScheduler() const { return getMotherBoard().getScheduler(); } Reactor& DeviceConfig::getReactor() const { return getMotherBoard().getReactor(); } GlobalSettings& DeviceConfig::getGlobalSettings() const { return getReactor().getGlobalSettings(); } const XMLElement& DeviceConfig::getChild(string_ref name) const { return getXML()->getChild(name); } const std::string& DeviceConfig::getChildData(string_ref name) const { return getXML()->getChildData(name); } string_ref DeviceConfig::getChildData(string_ref name, string_ref defaultValue) const { return getXML()->getChildData(name, defaultValue); } int DeviceConfig::getChildDataAsInt(string_ref name, int defaultValue) const { return getXML()->getChildDataAsInt(name, defaultValue); } bool DeviceConfig::getChildDataAsBool(string_ref name, bool defaultValue) const { return getXML()->getChildDataAsBool(name, defaultValue); } const XMLElement* DeviceConfig::findChild(string_ref name) const { return getXML()->findChild(name); } const std::string& DeviceConfig::getAttribute(string_ref attName) const { return getXML()->getAttribute(attName); } int DeviceConfig::getAttributeAsInt(string_ref attName, int defaultValue) const { return getXML()->getAttributeAsInt(attName, defaultValue); } } // namespace openmsx openmsx-0.10.0/src/config/SettingsConfig.cc0000644000175000017500000001135312262345041021340 0ustar manuelmanuel00000000000000#include "SettingsConfig.hh" #include "SettingsManager.hh" #include "XMLLoader.hh" #include "LocalFileReference.hh" #include "File.hh" #include "FileContext.hh" #include "FileException.hh" #include "CliComm.hh" #include "HotKey.hh" #include "CommandException.hh" #include "GlobalCommandController.hh" #include "Command.hh" #include "openmsx.hh" #include "memory.hh" #include using std::string; using std::vector; namespace openmsx { class SaveSettingsCommand : public Command { public: SaveSettingsCommand(CommandController& commandController, SettingsConfig& settingsConfig); virtual string execute(const vector& tokens); virtual string help (const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: SettingsConfig& settingsConfig; }; class LoadSettingsCommand : public Command { public: LoadSettingsCommand(CommandController& commandController, SettingsConfig& settingsConfig); virtual string execute(const vector& tokens); virtual string help (const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: SettingsConfig& settingsConfig; }; SettingsConfig::SettingsConfig( GlobalCommandController& globalCommandController, HotKey& hotKey_) : commandController(globalCommandController) , saveSettingsCommand(make_unique( commandController, *this)) , loadSettingsCommand(make_unique( commandController, *this)) , settingsManager(make_unique( globalCommandController)) , hotKey(hotKey_) , mustSaveSettings(false) { } SettingsConfig::~SettingsConfig() { if (mustSaveSettings) { try { saveSetting(); } catch (FileException& e) { commandController.getCliComm().printWarning( "Auto-saving of settings failed: " + e.getMessage() ); } } } void SettingsConfig::loadSetting(const FileContext& context, const string& filename) { LocalFileReference file(context.resolve(filename)); xmlElement = XMLLoader::load(file.getFilename(), "settings.dtd"); getSettingsManager().loadSettings(xmlElement); hotKey.loadBindings(xmlElement); // only set saveName after file was successfully parsed setSaveFilename(context, filename); } void SettingsConfig::setSaveFilename(const FileContext& context, const string& filename) { saveName = context.resolveCreate(filename); } void SettingsConfig::saveSetting(const string& filename) { const string& name = filename.empty() ? saveName : filename; if (name.empty()) return; // Normally the following isn't needed. Only when there was no // settings.xml in either the user or the system directory (so an // incomplete openMSX installation!!) the top level element will have // an empty name. And we shouldn't write an invalid xml file. xmlElement.setName("settings"); // settings are kept up-to-date hotKey.saveBindings(xmlElement); File file(name, File::TRUNCATE); string data = "\n" + xmlElement.dump(); file.write(data.data(), data.size()); } void SettingsConfig::setSaveSettings(bool save) { mustSaveSettings = save; } SettingsManager& SettingsConfig::getSettingsManager() { return *settingsManager; // *** } // class SaveSettingsCommand SaveSettingsCommand::SaveSettingsCommand( CommandController& commandController, SettingsConfig& settingsConfig_) : Command(commandController, "save_settings") , settingsConfig(settingsConfig_) { } string SaveSettingsCommand::execute(const vector& tokens) { try { switch (tokens.size()) { case 1: settingsConfig.saveSetting(); break; case 2: settingsConfig.saveSetting(tokens[1]); break; default: throw SyntaxError(); } } catch (FileException& e) { throw CommandException(e.getMessage()); } return ""; } string SaveSettingsCommand::help(const vector& /*tokens*/) const { return "Save the current settings."; } void SaveSettingsCommand::tabCompletion(vector& tokens) const { if (tokens.size() == 2) { completeFileName(tokens, SystemFileContext()); } } // class LoadSettingsCommand LoadSettingsCommand::LoadSettingsCommand( CommandController& commandController, SettingsConfig& settingsConfig_) : Command(commandController, "load_settings") , settingsConfig(settingsConfig_) { } string LoadSettingsCommand::execute(const vector& tokens) { if (tokens.size() != 2) { throw SyntaxError(); } settingsConfig.loadSetting(SystemFileContext(), tokens[1]); return ""; } string LoadSettingsCommand::help(const vector& /*tokens*/) const { return "Load settings from given file."; } void LoadSettingsCommand::tabCompletion(vector& tokens) const { if (tokens.size() == 2) { completeFileName(tokens, SystemFileContext()); } } } // namespace openmsx openmsx-0.10.0/src/config/XMLLoader.hh0000644000175000017500000000040612262345041020210 0ustar manuelmanuel00000000000000#ifndef XMLLOADER_HH #define XMLLOADER_HH #include "XMLElement.hh" namespace openmsx { namespace XMLLoader { XMLElement load(const std::string& filename, const std::string& systemID); } // namespace XMLLoader } // namespace openmsx #endif openmsx-0.10.0/src/config/ConfigException.hh0000644000175000017500000000041412262345041021504 0ustar manuelmanuel00000000000000#ifndef CONFIGEXCEPTION_HH #define CONFIGEXCEPTION_HH #include "MSXException.hh" namespace openmsx { class ConfigException : public MSXException { public: explicit ConfigException(string_ref message) : MSXException(message) {} }; } // namespace openmsx #endif openmsx-0.10.0/src/config/XMLElement.cc0000644000175000017500000002334212262345041020365 0ustar manuelmanuel00000000000000#include "XMLElement.hh" #include "StringOp.hh" #include "FileContext.hh" // for bwcompat #include "ConfigException.hh" #include "serialize.hh" #include "serialize_stl.hh" #include "memory.hh" #include "xrange.hh" #include #include #include using std::unique_ptr; using std::string; namespace openmsx { XMLElement::XMLElement(string_ref name_) : name(name_.str()) { } XMLElement::XMLElement(string_ref name_, string_ref data_) : name(name_.str()) , data(data_.str()) { } XMLElement& XMLElement::addChild(XMLElement child) { // Mixed-content elements are not supported by this class. In the past // we had a 'assert(data.empty())' here to enforce this, though that // assert triggered when you started openMSX without a user (but with // a system) settings.xml file (the deeper reason is a harmless comment // in the system version of this file). // When you add child nodes to a node with data, that data will be // ignored when this node is later written to disk. In the case of // settings.xml this behaviour is fine. children.push_back(std::move(child)); return children.back(); } void XMLElement::removeChild(const XMLElement& child) { assert(std::count_if(children.begin(), children.end(), [&](Children::value_type& v) { return &v == &child; }) == 1); auto it = std::find_if(children.begin(), children.end(), [&](Children::value_type& v) { return &v == &child; }); assert(it != children.end()); children.erase(it); } XMLElement::Attributes::iterator XMLElement::findAttribute(string_ref name) { return find_if(attributes.begin(), attributes.end(), [&](Attribute& a) { return a.first == name; }); } XMLElement::Attributes::const_iterator XMLElement::findAttribute(string_ref name) const { return find_if(attributes.begin(), attributes.end(), [&](const Attribute& a) { return a.first == name; }); } void XMLElement::addAttribute(string_ref name, string_ref value) { assert(findAttribute(name) == attributes.end()); attributes.push_back(make_pair(name.str(), value.str())); } void XMLElement::setAttribute(string_ref name, string_ref value) { auto it = findAttribute(name); if (it != attributes.end()) { it->second = value.str(); } else { attributes.push_back(make_pair(name.str(), value.str())); } } void XMLElement::removeAttribute(string_ref name) { auto it = findAttribute(name); if (it != attributes.end()) { attributes.erase(it); } } bool XMLElement::getDataAsBool() const { return StringOp::stringToBool(getData()); } int XMLElement::getDataAsInt() const { return StringOp::stringToInt(getData()); } double XMLElement::getDataAsDouble() const { return StringOp::stringToDouble(getData()); } void XMLElement::setName(string_ref name_) { name = name_.str(); } void XMLElement::clearName() { name.clear(); } void XMLElement::setData(string_ref data_) { assert(children.empty()); // no mixed-content elements data = data_.str(); } std::vector XMLElement::getChildren(string_ref name) const { std::vector result; for (auto& c : children) { if (c.getName() == name) { result.push_back(&c); } } return result; } XMLElement* XMLElement::findChild(string_ref name) { for (auto& c : children) { if (c.getName() == name) { return &c; } } return nullptr; } const XMLElement* XMLElement::findChild(string_ref name) const { return const_cast(this)->findChild(name); } const XMLElement* XMLElement::findNextChild(string_ref name, size_t& fromIndex) const { for (auto i : xrange(fromIndex, children.size())) { if (children[i].getName() == name) { fromIndex = i + 1; return &children[i]; } } for (auto i : xrange(fromIndex)) { if (children[i].getName() == name) { fromIndex = i + 1; return &children[i]; } } return nullptr; } XMLElement* XMLElement::findChildWithAttribute(string_ref name, string_ref attName, string_ref attValue) { for (auto& c : children) { if ((c.getName() == name) && (c.getAttribute(attName) == attValue)) { return &c; } } return nullptr; } const XMLElement* XMLElement::findChildWithAttribute(string_ref name, string_ref attName, string_ref attValue) const { return const_cast(this)->findChildWithAttribute( name, attName, attValue); } XMLElement& XMLElement::getChild(string_ref name) { if (auto* elem = findChild(name)) { return *elem; } throw ConfigException(StringOp::Builder() << "Missing tag \"" << name << "\"."); } const XMLElement& XMLElement::getChild(string_ref name) const { return const_cast(this)->getChild(name); } XMLElement& XMLElement::getCreateChild(string_ref name, string_ref defaultValue) { if (auto* result = findChild(name)) { return *result; } return addChild(XMLElement(name, defaultValue)); } XMLElement& XMLElement::getCreateChildWithAttribute( string_ref name, string_ref attName, string_ref attValue) { if (auto* result = findChildWithAttribute(name, attName, attValue)) { return *result; } auto& result = addChild(XMLElement(name)); result.addAttribute(attName, attValue); return result; } const string& XMLElement::getChildData(string_ref name) const { return getChild(name).getData(); } string_ref XMLElement::getChildData(string_ref name, string_ref defaultValue) const { auto* child = findChild(name); return child ? child->getData() : defaultValue; } bool XMLElement::getChildDataAsBool(string_ref name, bool defaultValue) const { auto* child = findChild(name); return child ? StringOp::stringToBool(child->getData()) : defaultValue; } int XMLElement::getChildDataAsInt(string_ref name, int defaultValue) const { auto* child = findChild(name); return child ? StringOp::stringToInt(child->getData()) : defaultValue; } void XMLElement::setChildData(string_ref name, string_ref value) { if (auto* child = findChild(name)) { child->setData(value); } else { addChild(XMLElement(name, value)); } } void XMLElement::removeAllChildren() { children.clear(); } bool XMLElement::hasAttribute(string_ref name) const { return findAttribute(name) != attributes.end(); } const string& XMLElement::getAttribute(string_ref attName) const { auto it = findAttribute(attName); if (it == attributes.end()) { throw ConfigException("Missing attribute \"" + attName + "\"."); } return it->second; } string_ref XMLElement::getAttribute(string_ref attName, string_ref defaultValue) const { auto it = findAttribute(attName); return (it == attributes.end()) ? defaultValue : it->second; } bool XMLElement::getAttributeAsBool(string_ref attName, bool defaultValue) const { auto it = findAttribute(attName); return (it == attributes.end()) ? defaultValue : StringOp::stringToBool(it->second); } int XMLElement::getAttributeAsInt(string_ref attName, int defaultValue) const { auto it = findAttribute(attName); return (it == attributes.end()) ? defaultValue : StringOp::stringToInt(it->second); } bool XMLElement::findAttributeInt(string_ref attName, unsigned& result) const { auto it = findAttribute(attName); if (it != attributes.end()) { result = StringOp::stringToInt(it->second); return true; } else { return false; } } string XMLElement::dump() const { StringOp::Builder result; dump(result, 0); return result; } void XMLElement::dump(StringOp::Builder& result, unsigned indentNum) const { string indent(indentNum, ' '); result << indent << '<' << getName(); for (auto& p : attributes) { result << ' ' << p.first << "=\"" << XMLEscape(p.second) << '"'; } if (children.empty()) { if (data.empty()) { result << "/>\n"; } else { result << '>' << XMLEscape(data) << "\n"; } } else { result << ">\n"; for (auto& c : children) { c.dump(result, indentNum + 2); } result << indent << "\n"; } } string XMLElement::XMLEscape(const string& str) { xmlChar* buffer = xmlEncodeEntitiesReentrant( nullptr, reinterpret_cast(str.c_str())); string result = reinterpret_cast(buffer); xmlFree(buffer); return result; } static unique_ptr lastSerializedFileContext; unique_ptr XMLElement::getLastSerializedFileContext() { return std::move(lastSerializedFileContext); // this also sets value to nullptr; } // version 1: initial version // version 2: removed 'context' tag // also removed 'parent', but that was never serialized // 2b: (no need to increase version) name and data members are // serialized as normal members instead of constructor parameters // 2c: (no need to increase version) attributes were initially stored as // map, later this was changed to // vector>. To keep bw-compat the serialize() // method converted between these two formats. Though (by luck) in // the XML output both datastructures are serialized to the same // format, so we can drop this conversion step without breaking // bw-compat. template void XMLElement::serialize(Archive& ar, unsigned version) { ar.serialize("name", name); ar.serialize("data", data); ar.serialize("attributes", attributes); ar.serialize("children", children); if (ar.versionBelow(version, 2)) { assert(ar.isLoader()); unique_ptr context; ar.serialize("context", context); if (context) { assert(!lastSerializedFileContext); lastSerializedFileContext = std::move(context); } } } INSTANTIATE_SERIALIZE_METHODS(XMLElement); } // namespace openmsx openmsx-0.10.0/src/config/XMLException.hh0000644000175000017500000000040012262345041020732 0ustar manuelmanuel00000000000000#ifndef XMLEXCEPTION_HH #define XMLEXCEPTION_HH #include "MSXException.hh" namespace openmsx { class XMLException : public MSXException { public: explicit XMLException(string_ref message) : MSXException(message) {} }; } // namespace openmsx #endif openmsx-0.10.0/src/EmuDuration.hh0000644000175000017500000000700512262345041017412 0ustar manuelmanuel00000000000000#ifndef EMUDUARTION_HH #define EMUDUARTION_HH #include #include namespace openmsx { // constants static const uint64_t MAIN_FREQ = 3579545ULL * 960; static const unsigned MAIN_FREQ32 = MAIN_FREQ; static_assert(MAIN_FREQ < (1ull << 32), "must fit in 32 bit"); class EmuDuration { public: // This is only a very small class (one 64-bit member). On 64-bit CPUs // it's cheaper to pass this by value. On 32-bit CPUs pass-by-reference // is cheaper. #ifdef __x86_64 typedef const EmuDuration param; #else typedef const EmuDuration& param; #endif // friends friend class EmuTime; // constructors EmuDuration() : time(0) {} explicit EmuDuration(uint64_t n) : time(n) {} explicit EmuDuration(double duration) : time(uint64_t(duration * MAIN_FREQ)) {} static EmuDuration sec(unsigned x) { return EmuDuration(x * MAIN_FREQ); } static EmuDuration msec(unsigned x) { return EmuDuration(x * MAIN_FREQ / 1000); } static EmuDuration usec(unsigned x) { return EmuDuration(x * MAIN_FREQ / 1000000); } static EmuDuration hz(unsigned x) { return EmuDuration(MAIN_FREQ / x); } // conversions double toDouble() const { return double(time) / MAIN_FREQ32; } uint64_t length() const { return time; } // assignment operator EmuDuration& operator=(EmuDuration::param d) { time = d.time; return *this; } // comparison operators bool operator==(EmuDuration::param d) const { return time == d.time; } bool operator!=(EmuDuration::param d) const { return time != d.time; } bool operator< (EmuDuration::param d) const { return time < d.time; } bool operator<=(EmuDuration::param d) const { return time <= d.time; } bool operator> (EmuDuration::param d) const { return time > d.time; } bool operator>=(EmuDuration::param d) const { return time >= d.time; } // arithmetic operators const EmuDuration operator%(EmuDuration::param d) const { return EmuDuration(time % d.time); } const EmuDuration operator+(EmuDuration::param d) const { return EmuDuration(time + d.time); } const EmuDuration operator*(unsigned fact) const { return EmuDuration(time * fact); } const EmuDuration operator/(unsigned fact) const { return EmuDuration(time / fact); } const EmuDuration divRoundUp(unsigned fact) const { return EmuDuration((time + fact - 1) / fact); } unsigned operator/(EmuDuration::param d) const { uint64_t result = time / d.time; #ifdef DEBUG // we don't even want this overhead in devel builds assert(result == unsigned(result)); #endif return unsigned(result); } unsigned divUp(EmuDuration::param d) const { uint64_t result = (time + d.time - 1) / d.time; #ifdef DEBUG assert(result == unsigned(result)); #endif return unsigned(result); } double div(EmuDuration::param d) const { return double(time) / d.time; } EmuDuration& operator*=(unsigned fact) { time *= fact; return *this; } EmuDuration& operator*=(double fact) { time = uint64_t(time * fact); return *this; } EmuDuration& operator/=(double fact) { time = uint64_t(time / fact); return *this; } // ticks // TODO: Used in WavAudioInput. Keep or use DynamicClock instead? unsigned getTicksAt(unsigned freq) const { uint64_t result = time / (MAIN_FREQ32 / freq); #ifdef DEBUG // we don't even want this overhead in devel builds assert(result == unsigned(result)); #endif return unsigned(result); } template void serialize(Archive& ar, unsigned version); static const EmuDuration zero; static const EmuDuration infinity; private: uint64_t time; }; } // namespace openmsx #endif openmsx-0.10.0/src/AndroidApiWrapper.hh0000644000175000017500000000061312262345041020527 0ustar manuelmanuel00000000000000#ifndef ANDROIDAPIWRAPPER_HH #include "build-info.hh" #if PLATFORM_ANDROID #include "MSXException.hh" namespace openmsx { class AndroidApiWrapper { public: static std::string getStorageDirectory(); }; class JniException : public MSXException { public: explicit JniException(string_ref message) : MSXException(message) {} }; } // namespace openmsx #endif // #if PLATFORM_ANDROID #endif openmsx-0.10.0/src/security/0000755000175000017500000000000012262345041016502 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/security/SspiUtils.hh0000644000175000017500000000423112262345041020762 0ustar manuelmanuel00000000000000#ifndef SSPI_UTILS_HH #define SSPI_UTILS_HH #ifdef _WIN32 #include #ifdef __GNUC__ // MinGW32 requires that subauth.h be included before security.h, in order to define several things // This differs from VC++, which only needs security.h #include // MinGW32 does not define NEGOSSP_NAME_W anywhere. It should. #define NEGOSSP_NAME_W L"Negotiate" #endif #ifndef SECURITY_WIN32 #define SECURITY_WIN32 #endif #include #include #include // // NOTE: This file MUST be kept in sync between the openmsx and openmsx-debugger projects // namespace openmsx { namespace sspiutils { const unsigned STREAM_ERROR = 0xffffffff; class StreamWrapper { public: virtual uint32_t Read (void* buffer, uint32_t cb) = 0; virtual uint32_t Write(void* buffer, uint32_t cb) = 0; }; class SspiPackageBase { protected: CredHandle hCreds; CtxtHandle hContext; StreamWrapper& stream; const unsigned int cbMaxTokenSize; SspiPackageBase(StreamWrapper& stream, const SEC_WCHAR* securityPackage); ~SspiPackageBase(); }; // Generic access control flags, used with AccessCheck const DWORD ACCESS_READ = 0x1; const DWORD ACCESS_WRITE = 0x2; const DWORD ACCESS_EXECUTE = 0x4; const DWORD ACCESS_ALL = ACCESS_READ | ACCESS_WRITE | ACCESS_EXECUTE; const GENERIC_MAPPING mapping = { ACCESS_READ, ACCESS_WRITE, ACCESS_EXECUTE, ACCESS_ALL }; void InitTokenContextBuffer(PSecBufferDesc pSecBufferDesc, PSecBuffer pSecBuffer); void ClearContextBuffers(PSecBufferDesc pSecBufferDesc); void DebugPrintSecurityStatus(const char* context, SECURITY_STATUS ss); void DebugPrintSecurityBool(const char* context, BOOL ret); void DebugPrintSecurityPackageName(PCtxtHandle phContext); void DebugPrintSecurityPrincipalName(PCtxtHandle phContext); void DebugPrintSecurityDescriptor(PSECURITY_DESCRIPTOR psd); PSECURITY_DESCRIPTOR CreateCurrentUserSecurityDescriptor(); unsigned long GetPackageMaxTokenSize(const SEC_WCHAR* package); bool SendChunk(StreamWrapper& stream, void* buffer, uint32_t cb); bool RecvChunk(StreamWrapper& stream, std::vector& buffer, uint32_t cbMaxSize); } // namespace sspiutils } // namespace openmsx #endif // _WIN32 #endif // SSPI_UTILS_HH openmsx-0.10.0/src/security/SspiNegotiateServer.hh0000644000175000017500000000071012262345041022766 0ustar manuelmanuel00000000000000#ifndef SSPI_NEGOTIATE_SERVER_HH #define SSPI_NEGOTIATE_SERVER_HH #ifdef _WIN32 #include "SspiUtils.hh" namespace openmsx { class SspiNegotiateServer : public sspiutils::SspiPackageBase { public: explicit SspiNegotiateServer(sspiutils::StreamWrapper& serverStream); ~SspiNegotiateServer(); bool Authenticate(); bool Authorize(); private: PSECURITY_DESCRIPTOR psd; }; } // namespace openmsx #endif // _WIN32 #endif // SSPI_NEGOTIATE_SERVER_HH openmsx-0.10.0/src/security/node.mk0000644000175000017500000000016712262345041017764 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ SocketStreamWrapper SspiNegotiateServer SspiUtils include build/node-end.mk openmsx-0.10.0/src/security/SocketStreamWrapper.cc0000644000175000017500000000117012262345041022755 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "SocketStreamWrapper.hh" namespace openmsx { using namespace sspiutils; SocketStreamWrapper::SocketStreamWrapper(SOCKET userSock) : sock(userSock) { } uint32_t SocketStreamWrapper::Read(void* buffer, uint32_t cb) { int recvd = recv(sock, static_cast(buffer), cb, 0); if (recvd == 0 || recvd == SOCKET_ERROR) { return STREAM_ERROR; } return recvd; } uint32_t SocketStreamWrapper::Write(void* buffer, uint32_t cb) { int sent = send(sock, static_cast(buffer), cb, 0); if (sent == 0 || sent == SOCKET_ERROR) { return STREAM_ERROR; } return sent; } } // namespace openmsx #endif openmsx-0.10.0/src/security/SspiNegotiateServer.cc0000644000175000017500000000753012262345041022763 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "SspiNegotiateServer.hh" #include "MSXException.hh" #include "openmsx.hh" namespace openmsx { using namespace sspiutils; SspiNegotiateServer::SspiNegotiateServer(StreamWrapper& serverStream) : SspiPackageBase(serverStream, NEGOSSP_NAME_W) { // We should probably cache the security descriptor, but this // isn't exactly a performance-sensitive part of the code psd = CreateCurrentUserSecurityDescriptor(); if (!psd) { throw MSXException("CreateCurrentUserSecurityDescriptor failed"); } } SspiNegotiateServer::~SspiNegotiateServer() { LocalFree(psd); } bool SspiNegotiateServer::Authenticate() { TimeStamp tsCredsExpiry; SECURITY_STATUS ss = AcquireCredentialsHandleW( nullptr, const_cast(NEGOSSP_NAME_W), SECPKG_CRED_INBOUND, nullptr, nullptr, nullptr, nullptr, &hCreds, &tsCredsExpiry); DebugPrintSecurityStatus("AcquireCredentialsHandleW", ss); if (ss != SEC_E_OK) { return false; } SecBufferDesc secClientBufferDesc, secServerBufferDesc; SecBuffer secClientBuffer, secServerBuffer; InitTokenContextBuffer(&secClientBufferDesc, &secClientBuffer); InitTokenContextBuffer(&secServerBufferDesc, &secServerBuffer); std::vector buffer; PCtxtHandle phContext = nullptr; while (true) { // Receive another buffer from the client PRT_DEBUG("Receiving client chunk"); bool ret = RecvChunk(stream, buffer, cbMaxTokenSize); if (!ret) { PRT_DEBUG("RecvChunk failed"); return false; } PRT_DEBUG("Received " << buffer.size() << " bytes"); secClientBuffer.cbBuffer = static_cast(buffer.size()); secClientBuffer.pvBuffer = &buffer[0]; ULONG fContextAttr; TimeStamp tsContextExpiry; ss = AcceptSecurityContext( &hCreds, phContext, &secClientBufferDesc, ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_CONNECTION, SECURITY_NETWORK_DREP, &hContext, &secServerBufferDesc, &fContextAttr, &tsContextExpiry); DebugPrintSecurityStatus("AcceptSecurityContext", ss); if (ss != SEC_E_OK && ss != SEC_I_CONTINUE_NEEDED) { return false; } // If we have something for the client, send it if (secServerBuffer.cbBuffer) { PRT_DEBUG("Sending " << secServerBuffer.cbBuffer << " bytes to client"); bool ret = SendChunk(stream, secServerBuffer.pvBuffer, secServerBuffer.cbBuffer); ClearContextBuffers(&secServerBufferDesc); if (!ret) { PRT_DEBUG("SendChunk failed"); return false; } } // SEC_E_OK means that we're done if (ss == SEC_E_OK) { DebugPrintSecurityPackageName(&hContext); DebugPrintSecurityPrincipalName(&hContext); return true; } // Another time around the loop phContext = &hContext; } } bool SspiNegotiateServer::Authorize() { #ifdef __GNUC__ // MinGW32's headers do not define QuerySecurityContextToken, // nor does its import library provide an export for it. // So when building with MinGW32, we load the export by hand. HMODULE secur32 = GetModuleHandleW(L"secur32.dll"); if (!secur32) { return false; } QUERY_SECURITY_CONTEXT_TOKEN_FN QuerySecurityContextToken = (QUERY_SECURITY_CONTEXT_TOKEN_FN)GetProcAddress(secur32, "QuerySecurityContextToken"); if (!QuerySecurityContextToken) { return false; } #endif HANDLE hClientToken; SECURITY_STATUS ss = QuerySecurityContextToken(&hContext, &hClientToken); DebugPrintSecurityStatus("QuerySecurityContextToken", ss); if (ss != SEC_E_OK) { return false; } PRIVILEGE_SET privilegeSet; DWORD dwPrivSetSize = sizeof(privilegeSet); DWORD dwGranted; BOOL fAccess; BOOL ret = AccessCheck( psd, hClientToken, ACCESS_ALL, (PGENERIC_MAPPING)&mapping, &privilegeSet, &dwPrivSetSize, &dwGranted, &fAccess); DebugPrintSecurityBool("AccessCheck", ret); PRT_DEBUG("Access " << (ret && fAccess ? "granted" : "denied")); DebugPrintSecurityPrincipalName(&hContext); CloseHandle(hClientToken); return ret && fAccess; } } // namespace openmsx #endif openmsx-0.10.0/src/security/SocketStreamWrapper.hh0000644000175000017500000000071612262345041022774 0ustar manuelmanuel00000000000000#ifndef SOCKET_STREAM_WRAPPER_HH #define SOCKET_STREAM_WRAPPER_HH #ifdef _WIN32 #include #include "SspiUtils.hh" namespace openmsx { class SocketStreamWrapper : public sspiutils::StreamWrapper { public: explicit SocketStreamWrapper(SOCKET userSock); uint32_t Read (void* buffer, uint32_t cb); uint32_t Write(void* buffer, uint32_t cb); private: SOCKET sock; }; } // namespace openmsx #endif // _WIN32 #endif // SOCKET_STREAM_WRAPPER_HH openmsx-0.10.0/src/security/SspiUtils.cc0000644000175000017500000001616612262345041020762 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "SspiUtils.hh" #include "MSXException.hh" #include #include #include "openmsx.hh" // // NOTE: This file MUST be kept in sync between the openmsx and openmsx-debugger projects // namespace openmsx { namespace sspiutils { SspiPackageBase::SspiPackageBase(StreamWrapper& userStream, const SEC_WCHAR* securityPackage) : stream(userStream) , cbMaxTokenSize(GetPackageMaxTokenSize(securityPackage)) { memset(&hCreds, 0, sizeof(hCreds)); memset(&hContext, 0, sizeof(hContext)); if (!cbMaxTokenSize) { throw MSXException("GetPackageMaxTokenSize failed"); } } SspiPackageBase::~SspiPackageBase() { DeleteSecurityContext(&hContext); FreeCredentialsHandle(&hCreds); } void InitTokenContextBuffer(PSecBufferDesc pSecBufferDesc, PSecBuffer pSecBuffer) { pSecBuffer->BufferType = SECBUFFER_TOKEN; pSecBuffer->cbBuffer = 0; pSecBuffer->pvBuffer = nullptr; pSecBufferDesc->ulVersion = SECBUFFER_VERSION; pSecBufferDesc->cBuffers = 1; pSecBufferDesc->pBuffers = pSecBuffer; } void ClearContextBuffers(PSecBufferDesc pSecBufferDesc) { for (ULONG i = 0; i < pSecBufferDesc->cBuffers; i ++) { FreeContextBuffer(pSecBufferDesc->pBuffers[i].pvBuffer); pSecBufferDesc->pBuffers[i].cbBuffer = 0; pSecBufferDesc->pBuffers[i].pvBuffer = nullptr; } } void DebugPrintSecurityStatus(const char* context, SECURITY_STATUS ss) { (void)&context; (void)&ss; #ifdef DEBUG switch (ss) { case SEC_E_OK: PRT_DEBUG(context << ": SEC_E_OK"); break; case SEC_I_CONTINUE_NEEDED: PRT_DEBUG(context << ": SEC_I_CONTINUE_NEEDED"); break; case SEC_E_INVALID_TOKEN: PRT_DEBUG(context << ": SEC_E_INVALID_TOKEN"); break; case SEC_E_BUFFER_TOO_SMALL: PRT_DEBUG(context << ": SEC_E_BUFFER_TOO_SMALL"); break; case SEC_E_INVALID_HANDLE: PRT_DEBUG(context << ": SEC_E_INVALID_HANDLE"); break; case SEC_E_WRONG_PRINCIPAL: PRT_DEBUG(context << ": SEC_E_WRONG_PRINCIPAL"); break; default: PRT_DEBUG(context << ": " << ss); break; } #endif } void DebugPrintSecurityBool(const char* context, BOOL ret) { (void)&context; (void)&ret; #ifdef DEBUG if (ret) { PRT_DEBUG(context << ": true"); } else { PRT_DEBUG(context << ": false - " << GetLastError()); } #endif } void DebugPrintSecurityPackageName(PCtxtHandle phContext) { (void)&phContext; #ifdef DEBUG SecPkgContext_PackageInfoA package; SECURITY_STATUS ss = QueryContextAttributesA(phContext, SECPKG_ATTR_PACKAGE_INFO, &package); if (ss == SEC_E_OK) { PRT_DEBUG("Using " << package.PackageInfo->Name << " package"); } #endif } void DebugPrintSecurityPrincipalName(PCtxtHandle phContext) { (void)&phContext; #ifdef DEBUG SecPkgContext_NamesA name; SECURITY_STATUS ss = QueryContextAttributesA(phContext, SECPKG_ATTR_NAMES, &name); if (ss == SEC_E_OK) { PRT_DEBUG("Client principal " << name.sUserName); } #endif } void DebugPrintSecurityDescriptor(PSECURITY_DESCRIPTOR psd) { (void)&psd; #ifdef DEBUG char* sddl; BOOL ret = ConvertSecurityDescriptorToStringSecurityDescriptorA( psd, SDDL_REVISION, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, &sddl, nullptr); if (ret) { PRT_DEBUG("SecurityDescriptor: " << sddl); LocalFree(sddl); } #endif } // If successful, caller must free the results with LocalFree() // If unsuccessful, returns null PTOKEN_USER GetProcessToken() { PTOKEN_USER pToken = nullptr; HANDLE hProcessToken; BOOL ret = OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hProcessToken); DebugPrintSecurityBool("OpenProcessToken", ret); if (ret) { DWORD cbToken; ret = GetTokenInformation(hProcessToken, TokenUser, nullptr, 0, &cbToken); assert(!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && cbToken); pToken = (TOKEN_USER*)LocalAlloc(LMEM_ZEROINIT, cbToken); if (pToken) { ret = GetTokenInformation(hProcessToken, TokenUser, pToken, cbToken, &cbToken); DebugPrintSecurityBool("GetTokenInformation", ret); if (!ret) { LocalFree(pToken); pToken = nullptr; } } CloseHandle(hProcessToken); } return pToken; } // If successful, caller must free the results with LocalFree() // If unsuccessful, returns null PSECURITY_DESCRIPTOR CreateCurrentUserSecurityDescriptor() { PSECURITY_DESCRIPTOR psd = nullptr; PTOKEN_USER pToken = GetProcessToken(); if (pToken) { PSID pUserSid = pToken->User.Sid; const DWORD cbEachAce = sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD); const DWORD cbACL = sizeof(ACL) + cbEachAce + GetLengthSid(pUserSid); // Allocate the SD and the ACL in one allocation, so we only have one buffer to manage // The SD structure ends with a pointer, so the start of the ACL will be well aligned BYTE* buffer = (BYTE*)LocalAlloc(LMEM_ZEROINIT, SECURITY_DESCRIPTOR_MIN_LENGTH + cbACL); if (buffer) { psd = (PSECURITY_DESCRIPTOR)buffer; PACL pacl = (PACL)(buffer + SECURITY_DESCRIPTOR_MIN_LENGTH); PACCESS_ALLOWED_ACE pUserAce; if (InitializeSecurityDescriptor(psd, SECURITY_DESCRIPTOR_REVISION) && InitializeAcl(pacl, cbACL, ACL_REVISION) && AddAccessAllowedAce(pacl, ACL_REVISION, ACCESS_ALL, pUserSid) && SetSecurityDescriptorDacl(psd, TRUE, pacl, FALSE) && // Need to set the Group and Owner on the SD in order to use it with AccessCheck() GetAce(pacl, 0, (void**)&pUserAce) && SetSecurityDescriptorGroup(psd, &pUserAce->SidStart, FALSE) && SetSecurityDescriptorOwner(psd, &pUserAce->SidStart, FALSE)) { buffer = nullptr; } else { psd = nullptr; } LocalFree(buffer); } LocalFree(pToken); } if (psd) { assert(IsValidSecurityDescriptor(psd)); DebugPrintSecurityDescriptor(psd); } return psd; } unsigned long GetPackageMaxTokenSize(const SEC_WCHAR* package) { PSecPkgInfoW pkgInfo; SECURITY_STATUS ss = QuerySecurityPackageInfoW(const_cast(package), &pkgInfo); DebugPrintSecurityStatus("QuerySecurityPackageInfoW", ss); if (ss != SEC_E_OK) { return 0; } unsigned long cbMaxToken = pkgInfo->cbMaxToken; FreeContextBuffer(pkgInfo); return cbMaxToken; } bool Send(StreamWrapper& stream, void* buffer, uint32_t cb) { uint32_t sent = 0; while (sent < cb) { uint32_t ret = stream.Write((char*)buffer + sent, cb - sent); if (ret == STREAM_ERROR) { return false; } sent += ret; } return true; } bool SendChunk(StreamWrapper& stream, void* buffer, uint32_t cb) { uint32_t nl = htonl(cb); if (!Send(stream, &nl, sizeof(nl))) { return false; } return Send(stream, buffer, cb); } bool Recv(StreamWrapper& stream, void* buffer, uint32_t cb) { uint32_t recvd = 0; while (recvd < cb) { uint32_t ret = stream.Read((char*)buffer + recvd, cb - recvd); if (ret == STREAM_ERROR) { return false; } recvd += ret; } return true; } bool RecvChunkSize(StreamWrapper& stream, uint32_t* pcb) { uint32_t cb; bool ret = Recv(stream, &cb, sizeof(cb)); if (ret) { *pcb = ntohl(cb); } return ret; } bool RecvChunk(StreamWrapper& stream, std::vector& buffer, uint32_t cbMaxSize) { uint32_t cb; if (!RecvChunkSize(stream, &cb) || cb > cbMaxSize) { return false; } buffer.resize(cb); if (!Recv(stream, &buffer[0], cb)) { return false; } return true; } } // namespace sspiutils } // namespace openmsx #endif openmsx-0.10.0/src/CLIOption.hh0000644000175000017500000000142312262345041016754 0ustar manuelmanuel00000000000000#ifndef CLIOPTION_HH #define CLIOPTION_HH #include "string_ref.hh" #include namespace openmsx { class CLIOption { public: virtual ~CLIOption() {} virtual void parseOption(const std::string& option, std::deque& cmdLine) = 0; virtual string_ref optionHelp() const = 0; protected: std::string getArgument(const std::string& option, std::deque& cmdLine) const; std::string peekArgument(const std::deque& cmdLine) const; }; class CLIFileType { public: virtual ~CLIFileType() {} virtual void parseFileType(const std::string& filename, std::deque& cmdLine) = 0; virtual string_ref fileTypeHelp() const = 0; }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXException.hh0000644000175000017500000000100112262345041017472 0ustar manuelmanuel00000000000000#ifndef MSXEXCEPTION_HH #define MSXEXCEPTION_HH #include "string_ref.hh" namespace openmsx { class MSXException { public: explicit MSXException(string_ref message); ~MSXException(); const std::string& getMessage() const { return message; } private: const std::string message; }; class FatalError { public: explicit FatalError(string_ref message); ~FatalError(); const std::string& getMessage() const { return message; } private: const std::string message; }; } // namespace openmsx #endif openmsx-0.10.0/src/Connector.cc0000644000175000017500000000405312262345041017076 0ustar manuelmanuel00000000000000#include "Connector.hh" #include "Pluggable.hh" #include "PluggingController.hh" #include "serialize.hh" #include "CliComm.hh" namespace openmsx { Connector::Connector(PluggingController& pluggingController_, string_ref name_, std::unique_ptr dummy_) : pluggingController(pluggingController_) , name(name_.str()) , dummy(std::move(dummy_)) { plugged = dummy.get(); pluggingController.registerConnector(*this); } Connector::~Connector() { pluggingController.unregisterConnector(*this); } const std::string& Connector::getName() const { return name; } void Connector::plug(Pluggable& device, EmuTime::param time) { device.plug(*this, time); plugged = &device; // not executed if plug fails } void Connector::unplug(EmuTime::param time) { plugged->unplug(time); plugged = dummy.get(); } Pluggable& Connector::getPlugged() const { return *plugged; } template void Connector::serialize(Archive& ar, unsigned /*version*/) { std::string plugName; if (!ar.isLoader() && (plugged != dummy.get())) { plugName = plugged->getName(); } ar.serialize("plugName", plugName); if (!ar.isLoader()) { if (!plugName.empty()) { ar.beginSection(); ar.serializePolymorphic("pluggable", *plugged); ar.endSection(); } } else { if (plugName.empty()) { // was not plugged in plugged = dummy.get(); } else if (Pluggable* pluggable = pluggingController.findPluggable(plugName)) { plugged = pluggable; // set connector before loading the pluggable so that // the pluggable can test whether it was connected pluggable->setConnector(this); ar.skipSection(false); ar.serializePolymorphic("pluggable", *plugged); } else { // was plugged, but we don't have that pluggable anymore pluggingController.getCliComm().printWarning( "Pluggable \"" + plugName + "\" was plugged in, " "but is not available anymore on this system, " "so it will be ignored."); ar.skipSection(true); plugged = dummy.get(); } } } INSTANTIATE_SERIALIZE_METHODS(Connector); } // namespace openmsx openmsx-0.10.0/src/I8255Interface.hh0000644000175000017500000000155112262345041017513 0ustar manuelmanuel00000000000000#ifndef I8255INTERFACE_HH #define I8255INTERFACE_HH #include "EmuTime.hh" #include "openmsx.hh" namespace openmsx { class I8255Interface { public: virtual byte readA(EmuTime::param time) = 0; virtual byte readB(EmuTime::param time) = 0; virtual nibble readC0(EmuTime::param time) = 0; virtual nibble readC1(EmuTime::param time) = 0; virtual byte peekA(EmuTime::param time) const = 0; virtual byte peekB(EmuTime::param time) const = 0; virtual nibble peekC0(EmuTime::param time) const = 0; virtual nibble peekC1(EmuTime::param time) const = 0; virtual void writeA(byte value, EmuTime::param time) = 0; virtual void writeB(byte value, EmuTime::param time) = 0; virtual void writeC0(nibble value, EmuTime::param time) = 0; virtual void writeC1(nibble value, EmuTime::param time) = 0; protected: virtual ~I8255Interface() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXVictorHC9xSystemControl.hh0000644000175000017500000000117512262345041022260 0ustar manuelmanuel00000000000000#ifndef MSXVICTORHC9XSYSTEMCONTROL_HH #define MSXVICTORHC9XSYSTEMCONTROL_HH #include "MSXDevice.hh" namespace openmsx { class MSXVictorHC9xSystemControl : public MSXDevice { public: explicit MSXVictorHC9xSystemControl(const DeviceConfig& config); virtual ~MSXVictorHC9xSystemControl(); virtual byte readMem(word address, EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte systemControlRegister; }; } // namespace openmsx #endif openmsx-0.10.0/src/Version.hh0000644000175000017500000000056412262345041016606 0ustar manuelmanuel00000000000000#ifndef VERSION_HH #define VERSION_HH #include namespace openmsx { class Version { public: // Defined by build system: static const bool RELEASE; static const char* const VERSION; static const char* const REVISION; static const char* const BUILD_FLAVOUR; // Computed using constants above: static std::string full(); }; } // namespace openmsx #endif openmsx-0.10.0/src/InitException.hh0000644000175000017500000000057412262345041017744 0ustar manuelmanuel00000000000000#ifndef INITEXCEPTION_HH #define INITEXCEPTION_HH #include "MSXException.hh" namespace openmsx { /** Thrown when a subsystem initialisation fails. * For example: opening video surface, opening audio output etc. */ class InitException: public MSXException { public: explicit InitException(string_ref message) : MSXException(message) {} }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXS1990.hh0000644000175000017500000000175312262345041016277 0ustar manuelmanuel00000000000000#ifndef MSXS1990_HH #define MSXS1990_HH #include "MSXDevice.hh" #include namespace openmsx { class FirmwareSwitch; class S1990Debuggable; /** * This class implements the MSX-engine found in a MSX Turbo-R (S1990) * * TODO explanation */ class MSXS1990 : public MSXDevice { public: explicit MSXS1990(const DeviceConfig& config); virtual ~MSXS1990(); virtual void reset(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte readRegister(byte reg) const; void writeRegister(byte reg, byte value); void setCPUStatus(byte value); const std::unique_ptr firmwareSwitch; const std::unique_ptr debuggable; byte registerSelect; byte cpuStatus; friend class S1990Debuggable; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/0000755000175000017500000000000012262345041015552 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/file/ZlibInflate.hh0000644000175000017500000000103712262345041020277 0ustar manuelmanuel00000000000000#ifndef ZLIBINFLATE_HH #define ZLIBINFLATE_HH #include "openmsx.hh" #include #include namespace openmsx { template class MemBuffer; class ZlibInflate { public: ZlibInflate(const byte* buffer, size_t len); ~ZlibInflate(); void skip(size_t num); byte getByte(); unsigned get16LE(); unsigned get32LE(); std::string getString(size_t len); std::string getCString(); void inflate(MemBuffer& output, size_t sizeHint = 65536); private: z_stream s; bool wasInit; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/node.mk0000644000175000017500000000051612262345041017032 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ File \ FileContext \ Filename \ FileBase \ LocalFile \ FileOperations \ CompressedFileAdapter \ GZFileAdapter \ ZipFileAdapter \ ZlibInflate \ ReadDir \ FilePool \ PreCacheFile \ LocalFileReference HDR_ONLY:= \ FileException \ FileNotFoundException include build/node-end.mk openmsx-0.10.0/src/file/File.hh0000644000175000017500000001015012262345041016747 0ustar manuelmanuel00000000000000#ifndef FILE_HH #define FILE_HH #include "sha1.hh" #include "openmsx.hh" #include "noncopyable.hh" #include "string_ref.hh" #include #include namespace openmsx { class Filename; class FileBase; class FilePool; class File : private noncopyable { public: enum OpenMode { NORMAL, TRUNCATE, CREATE, LOAD_PERSISTENT, SAVE_PERSISTENT, PRE_CACHE, }; /** Create file object and open underlying file. * @param filename Name of the file to be opened. * @param mode Mode to open the file in: * @throws FileNotFoundException if file not found * @throws FileException for other errors */ explicit File(string_ref filename, OpenMode mode = NORMAL); explicit File(const Filename& filename, OpenMode mode = NORMAL); /** This constructor maps very closely on the fopen() libc function. * Compared to constructor above, it does not transparantly * uncompress files. * @param filename Name of the file to be opened. * @param mode Open mode, same meaning as in fopen(), but we assert * that it contains a 'b' character. */ File(string_ref filename, const char* mode); File(const Filename& filename, const char* mode); ~File(); /** Read from file. * @param buffer Destination address * @param num Number of bytes to read * @throws FileException */ void read(void* buffer, size_t num); /** Write to file. * @param buffer Source address * @param num Number of bytes to write * @throws FileException */ void write(const void* buffer, size_t num); /** Map file in memory. * @param size Filled in with filesize. * @result Pointer to memory block. * @throws FileException */ const byte* mmap(size_t& size); /** Unmap file from memory. */ void munmap(); /** Returns the size of this file * @result The size of this file * @throws FileException */ size_t getSize(); /** Move read/write pointer to the specified position. * @param pos Position in bytes from the beginning of the file. * @throws FileException */ void seek(size_t pos); /** Get the current position of the read/write pointer. * @result Position in bytes from the beginning of the file. * @throws FileException */ size_t getPos(); /** Truncate file size. Enlarging file size always works, but * making file smaller doesn't work on some platforms (windows) * @throws FileException */ void truncate(size_t size); /** Force a write of all buffered data to disk. There is no need to * call this function before destroying a File object. */ void flush(); /** Returns the URL of this file object. * @throws FileException */ const std::string getURL() const; /** Get Original filename for this object. This will usually just * return the filename portion of the URL. However for compressed * files this will be different. * @result Original file name * @throws FileException */ const std::string getOriginalName(); /** Check if this file is readonly * @result true iff file is readonly * @throws FileException */ bool isReadOnly() const; /** Get the date/time of last modification * @throws FileException */ time_t getModificationDate(); /** Calculate sha1sum of this file. * * If the FilePool was set (see setFilePool()), the calculation can * possibly be avoided by using the pool as a cache. * Note that currently it's even an error to call this method without * first having called setFilePool(), we might change this in the * future. */ Sha1Sum getSha1Sum(); /** Set FilePool, see also getSha1Sum() * FilePool is used to lookup/store sha1sum<->filename mappings. But * also to invalidate these mappings on writes to this file (the file * modification date is used as well to detect writes). */ void setFilePool(FilePool& filepool); private: friend class LocalFileReference; /** This is an internal method used by LocalFileReference. * Returns the path to the (uncompressed) file on the local, * filesystem. Or an empty string in case there is no such path. */ const std::string getLocalReference() const; const std::unique_ptr file; FilePool* filepool; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/PreCacheFile.hh0000644000175000017500000000117012262345041020344 0ustar manuelmanuel00000000000000#ifndef PRECACHEFILE_HH #define PRECACHEFILE_HH #include "Thread.hh" #include "Semaphore.hh" #include namespace openmsx { class FileBase; /** * Read the complete file once and discard result. Hopefully the file * sticks in the OS cache. Mainly useful to avoid CDROM spinups or to * speed up real floppy disk (/dev/fd0) reads. */ class PreCacheFile : private Runnable { public: explicit PreCacheFile(const std::string& name); ~PreCacheFile(); private: // Runnable virtual void run(); const std::string name; Thread thread; Semaphore sem; bool exitLoop; // locked by sem }; } // namespace openmsx #endif openmsx-0.10.0/src/file/LocalFileReference.hh0000644000175000017500000000347412262345041021554 0ustar manuelmanuel00000000000000#ifndef LOCALFILEREFERENCE_HH #define LOCALFILEREFERENCE_HH #include "Filename.hh" #include "noncopyable.hh" #include namespace openmsx { class File; /** Helper class to use files is APIs other than openmsx::File. * The openMSX File class has support for (g)zipped files (or maybe in the * future files over http, ftp, ...). Sometimes you need to pass a filename * to an API that doesn't support this (for example SDL_LoadWav()). This * class allows to create a temporary local uncompressed version of such * files. Use it like this: * * LocalFileReference file(filename); // can be any filename supported * // by openmsx::File * my_function(file.getFilename()); // my_function() can now work on * // a regular local file * * Note: In the past this functionality was available in the openmsx::File * class. The current implementation of that class always keep an open * file reference to the corresponding file. This gave problems on * (some versions of) windows if the external function tries to open * the file in read-write mode (for example IMG_Load() does this). The * implementation of this class does not keep a reference to the file. */ class LocalFileReference : private noncopyable { public: explicit LocalFileReference(const Filename& filename); explicit LocalFileReference(const std::string& url); explicit LocalFileReference(File& file); ~LocalFileReference(); /** Returns path to a local uncompressed version of this file. * This path only remains valid as long as this object is in scope. */ const std::string getFilename() const; private: void init(const std::string& url); void init(File& url); std::string tmpFile; std::string tmpDir; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/FileOperations.hh0000644000175000017500000002101012262345041021010 0ustar manuelmanuel00000000000000#ifndef FILEOPERATIONS_HH #define FILEOPERATIONS_HH #include "string_ref.hh" #include "unistdp.hh" // needed for mode_t definition when building with VC++ #include "statp.hh" #include #include namespace openmsx { namespace FileOperations { const char nativePathSeparator = #ifdef _WIN32 '\\'; #else '/'; #endif /** * Expand the '~' character to the users home directory * @param path Pathname, with or without '~' character * @result The expanded pathname */ std::string expandTilde(string_ref path); /** * Create the specified directory. Does some sanity checks so that * it bahaves the same on all platforms. The mode parameter is ignored * on windows. For compatibility with *nix creating the root dir (or a * drivename) is not an error instead the operation is silently * ignored. This function can only create one dircetory at-a-time. You * probably want to use the mkdirp function (see below). * @param path The path of the directory to create * @param mode The permission bits (*nix only) * @throw FileException */ void mkdir(const std::string& path, mode_t mode); /** * Acts like the unix command "mkdir -p". Creates the * specified directory, including the parent directories. * @param path The path of the directory to create * @throw FileException */ void mkdirp(string_ref path); /** * Call unlink() in a platform-independent manner */ int unlink(const std::string& path); /** * Call rmdir() in a platform-independent manner */ int rmdir(const std::string& path); /** Recurively delete a file or directory and (in case of a directory) * all its sub-components. */ int deleteRecursive(const std::string& path); /** Call fopen() in a platform-independent manner * @param filename the file path * @param mode the mode parameter, same as fopen * @result A pointer to the opened file, or nullptr on error * On error the global variable 'errno' is filled in (see * man fopen for details). */ FILE* openFile(const std::string& filename, const std::string& mode); /** * Open an ofstream in a platform-independent manner * @param stream an ofstream * @param filename the file path */ void openofstream(std::ofstream& stream, const std::string& filename); /** * Open an ofstream in a platform-independent manner * @param stream an ofstream * @param filename the file path * @param mode the open mode */ void openofstream(std::ofstream& stream, const std::string& filename, std::ios_base::openmode mode); /** * Returns the file portion of a path name. * @param path The pathname * @result The file portion */ string_ref getFilename(string_ref path); /** * Returns the directory portion of a path. * @param path The pathname * @result The directory portion. This includes the ending '/'. * If path doesn't have a directory portion the result * is an empty string. */ string_ref getBaseName(string_ref path); /** * Returns the extension portion of a path. * @param path The pathname * @result The extension portion. This excludes the '.'. * If path doesn't have an extension portion the result * is an empty string. */ string_ref getExtension(string_ref path); /** * Returns the path without extension. * @param path The pathname * @result The path without extension. This excludes the '.'. * If path doesn't have an extension portion the result * remains unchanged. */ string_ref stripExtension(string_ref path); /** Join two paths. * Returns the equivalent of 'path1 + '/' + path2'. If 'part2' is an * absolute path, that path is returned ('part1' is ignored). If * 'part1' is empty or if it already ends with '/', there will be no * extra '/' added inbetween 'part1' and 'part2'. */ std::string join(string_ref part1, string_ref part2); std::string join(string_ref part1, string_ref part2, string_ref part3); std::string join(string_ref part1, string_ref part2, string_ref part3, string_ref part4); /** * Returns the path in conventional path-delimiter. * @param path The pathname. * @result The path in conventional path-delimiter. * On UNI*Y systems, it will have no effect indeed. * Just for portability issue. (Especially for Win32) */ std::string getConventionalPath(string_ref path); /** * Returns the path in native path-delimiter. * @param path The pathname. * @result The path in native path-delimiter. * On UNI*Y systems, it will have no effect indeed. * Just for portability issue. (Especially for Win32) */ std::string getNativePath(string_ref path); /** Returns the current working directory. * @throw FileException (for example when directory has been deleted). */ std::string getCurrentWorkingDirectory(); /** Transform given path into an absolute path * @throw FileException */ std::string getAbsolutePath(string_ref path); /** * Checks whether it's a absolute path or not. * @param path The pathname. */ bool isAbsolutePath(string_ref path); /** * Get user's home directory. * @param username The name of the user * @result Home directory of the user or empty string in case of error * UNI*Y: get from env var "HOME" or from /etc/passwd * empty string means current user * Win32: Currently use "My Documents" as home directory. * Not "Documents and Settings". * This is because to support Win9x. * Ignores the username parameter */ std::string getUserHomeDir(string_ref username); /** * Get the openMSX dir in the user's home directory. * Default value is "~/.openMSX" (UNIX) or "~/openMSX" (win) */ const std::string& getUserOpenMSXDir(); /** * Get the openMSX data dir in the user's home directory. * Default value is "~/.openMSX/share" (UNIX) or "~/openMSX/share" (win) */ std::string getUserDataDir(); /** * Get system directory. * UNI*Y: statically defined as "/opt/openMSX/share". * Win32: use "same directory as .exe" + "/share". */ std::string getSystemDataDir(); /** * Get the current directory of the specified drive * Linux: just return an empty string */ std::string expandCurrentDirFromDrive(string_ref path); #ifdef _WIN32 typedef struct _stat Stat; #else typedef struct stat Stat; #endif /** * Call stat() and return the stat structure * @param filename the file path (will be tilde expanded) * @param st The stat structute that will be filled in * @result true iff success */ bool getStat(const std::string& filename, Stat& st); /** * Is this a regular file (no directory, device, ..)? */ bool isRegularFile(const std::string& filename); bool isRegularFile(const Stat& st); /** * Is this a directory? */ bool isDirectory(const std::string& directory); bool isDirectory(const Stat& st); /** * Does this file (directory) exists? */ bool exists(const std::string& filename); /** Get the date/time of last modification */ time_t getModificationDate(const Stat& st); /** * Gets the next numbered file name with the specified prefix in the * specified directory, with the specified extension. Examples: * automatic numbering of filenames for new screenshots or sound logs. * @param directory Name of the directory in the openMSX user dir in * which should be searched for the next filename * @param prefix Prefix of the filename with numbers * @param extension Extension of the filename with numbers */ std::string getNextNumberedFileName( string_ref directory, string_ref prefix, string_ref extension); /** Helper function for parsing filename arguments in Tcl commands. * - If argument is empty then getNextNumberedFileName() is used * with given directory, prefix and extension. * - If argument doesn't already end with the given extension that * extension is appended. * - If argument doesn't already include a directory, the given * directory is used (and created if required). */ std::string parseCommandFileArgument( string_ref argument, string_ref directory, string_ref prefix, string_ref extension); /** * Get the name of the temp directory on the system. * Typically /tmp on *nix and C:/WINDOWS/TEMP on windows */ std::string getTempDir(); /** * Open a new file with a unique name in the provided directory * @param directory directory in which to open the temp file * @param filename [output param] the name of the resulting file * @result pointer to the opened file */ FILE* openUniqueFile(const std::string& directory, std::string& filename); } // namespace FileOperations } // namespace openmsx #endif openmsx-0.10.0/src/file/FileContext.hh0000644000175000017500000000224612262345041020323 0ustar manuelmanuel00000000000000#ifndef FILECONTEXT_HH #define FILECONTEXT_HH #include "string_ref.hh" #include namespace openmsx { class FileContext { public: const std::string resolve (string_ref filename) const; const std::string resolveCreate(string_ref filename) const; std::vector getPaths() const; bool isUserContext() const; template void serialize(Archive& ar, unsigned version); protected: std::vector paths; std::vector savePaths; }; class ConfigFileContext : public FileContext { public: ConfigFileContext(string_ref path, string_ref hwDescr, string_ref userName); }; class SystemFileContext : public FileContext { public: SystemFileContext(); }; class PreferSystemFileContext : public FileContext { public: PreferSystemFileContext(); }; class UserFileContext : public FileContext { public: explicit UserFileContext(string_ref savePath = ""); }; class UserDataFileContext : public FileContext { public: explicit UserDataFileContext(string_ref subdir); }; class CurrentDirFileContext : public FileContext { public: CurrentDirFileContext(); }; } // namespace openmsx #endif openmsx-0.10.0/src/file/FileContext.cc0000644000175000017500000001134412262345041020310 0ustar manuelmanuel00000000000000#include "FileContext.hh" #include "FileOperations.hh" #include "FileException.hh" #include "StringOp.hh" #include "serialize.hh" #include "serialize_stl.hh" #include "openmsx.hh" #include #include using std::string; using std::vector; namespace openmsx { const string USER_DIRS = "{{USER_DIRS}}"; const string USER_OPENMSX = "{{USER_OPENMSX}}"; const string USER_DATA = "{{USER_DATA}}"; const string SYSTEM_DATA = "{{SYSTEM_DATA}}"; static string subst(string_ref path, string_ref before, string_ref after) { assert(path.starts_with(before)); return after + path.substr(before.size()); } static vector getPathsHelper(const vector& input) { vector result; for (auto& s : input) { if (StringOp::startsWith(s, USER_OPENMSX)) { result.push_back(subst(s, USER_OPENMSX, FileOperations::getUserOpenMSXDir())); } else if (StringOp::startsWith(s, USER_DATA)) { result.push_back(subst(s, USER_DATA, FileOperations::getUserDataDir())); } else if (StringOp::startsWith(s, SYSTEM_DATA)) { result.push_back(subst(s, SYSTEM_DATA, FileOperations::getSystemDataDir())); } else if (s == USER_DIRS) { // Nothing. Keep USER_DIRS for isUserContext() } else { result.push_back(s); } } return result; } static string resolveHelper(const vector& pathList, string_ref filename) { PRT_DEBUG("Context: " << filename); string filepath = FileOperations::expandCurrentDirFromDrive(filename); filepath = FileOperations::expandTilde(filepath); if (FileOperations::isAbsolutePath(filepath)) { // absolute path, don't resolve return filepath; } for (auto& p : pathList) { string name = FileOperations::join(p, filename); name = FileOperations::expandTilde(name); PRT_DEBUG("Context: try " << name); if (FileOperations::exists(name)) { return name; } } // not found in any path throw FileException(filename + " not found in this context"); } const string FileContext::resolve(string_ref filename) const { vector pathList = getPathsHelper(paths); string result = resolveHelper(pathList, filename); assert(FileOperations::expandTilde(result) == result); return result; } const string FileContext::resolveCreate(string_ref filename) const { string result; vector pathList = getPathsHelper(savePaths); try { result = resolveHelper(pathList, filename); } catch (FileException&) { string path = pathList.front(); try { FileOperations::mkdirp(path); } catch (FileException& e) { PRT_DEBUG(e.getMessage()); (void)&e; // Prevent warning } result = FileOperations::join(path, filename); } assert(FileOperations::expandTilde(result) == result); return result; } vector FileContext::getPaths() const { return getPathsHelper(paths); } bool FileContext::isUserContext() const { return find(paths.begin(), paths.end(), USER_DIRS) != paths.end(); } /// static string backSubstSymbols(const string& path) { string systemData = FileOperations::getSystemDataDir(); if (StringOp::startsWith(path, systemData)) { return subst(path, systemData, SYSTEM_DATA); } string userData = FileOperations::getSystemDataDir(); if (StringOp::startsWith(path, userData)) { return subst(path, userData, SYSTEM_DATA); } string userDir = FileOperations::getUserOpenMSXDir(); if (StringOp::startsWith(path, userDir)) { return subst(path, userDir, USER_OPENMSX); } // TODO USER_DIRS (not needed ATM) return path; } ConfigFileContext::ConfigFileContext(string_ref path, string_ref hwDescr, string_ref userName) { paths.push_back(backSubstSymbols(FileOperations::expandTilde(path))); savePaths.push_back(FileOperations::join( USER_OPENMSX, "persistent", hwDescr, userName)); } SystemFileContext::SystemFileContext() { paths.push_back(USER_DATA); paths.push_back(SYSTEM_DATA); savePaths.push_back(USER_DATA); } PreferSystemFileContext::PreferSystemFileContext() { paths.push_back(SYSTEM_DATA); // first system dir paths.push_back(USER_DATA); } UserFileContext::UserFileContext(string_ref savePath) { paths.push_back(""); paths.push_back(USER_DIRS); if (!savePath.empty()) { savePaths.push_back(FileOperations::join( USER_OPENMSX, "persistent", savePath)); } } UserDataFileContext::UserDataFileContext(string_ref subDir) { paths.push_back(""); paths.push_back(USER_OPENMSX + '/' + subDir); } CurrentDirFileContext::CurrentDirFileContext() { paths.push_back(""); } template void FileContext::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("paths", paths); ar.serialize("savePaths", savePaths); } INSTANTIATE_SERIALIZE_METHODS(FileContext); } // namespace openmsx openmsx-0.10.0/src/file/ReadDir.hh0000644000175000017500000000135712262345041017413 0ustar manuelmanuel00000000000000#ifndef READDIR_HH #define READDIR_HH #include "noncopyable.hh" #include "direntp.hh" #include #include namespace openmsx { /** * Simple wrapper around openmdir() / readdir() / closedir() functions. * Mainly usefull to automatically call closedir() when object goes out * of scope. */ class ReadDir : private noncopyable { public: explicit ReadDir(const std::string& directory); ~ReadDir(); /** Get directory entry for next file. Returns nullptr when there * are no more entries or in case of error (e.g. given directory * does not exist). */ struct dirent* getEntry(); /** Is the given directory valid (does it exist)? */ bool isValid() const; private: DIR* dir; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/LocalFile.hh0000644000175000017500000000225212262345041017726 0ustar manuelmanuel00000000000000#ifndef LOCALFILE_HH #define LOCALFILE_HH #if defined _WIN32 #include #endif #include "File.hh" #include "FileBase.hh" #include "systemfuncs.hh" #include #include namespace openmsx { class PreCacheFile; class LocalFile : public FileBase { public: LocalFile(string_ref filename, File::OpenMode mode); LocalFile(string_ref filename, const char* mode); virtual ~LocalFile(); virtual void read (void* buffer, size_t num); virtual void write(const void* buffer, size_t num); #if HAVE_MMAP || defined _WIN32 virtual const byte* mmap(size_t& size); virtual void munmap(); #endif virtual size_t getSize(); virtual void seek(size_t pos); virtual size_t getPos(); #if HAVE_FTRUNCATE virtual void truncate(size_t size); #endif virtual void flush(); virtual const std::string getURL() const; virtual const std::string getLocalReference(); virtual bool isReadOnly() const; virtual time_t getModificationDate(); void preCacheFile(); private: std::string filename; FILE* file; #if HAVE_MMAP byte* mmem; #endif #if defined _WIN32 byte* mmem; HANDLE hMmap; #endif std::unique_ptr cache; bool readOnly; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/Filename.cc0000644000175000017500000000267312262345041017611 0ustar manuelmanuel00000000000000#include "Filename.hh" #include "FileContext.hh" #include "FileOperations.hh" #include "MSXException.hh" #include "serialize.hh" #include using std::string; namespace openmsx { // dummy constructor, to be able to serialize vector Filename::Filename() { } Filename::Filename(const string& filename) : originalFilename(filename) , resolvedFilename(filename) { } Filename::Filename(const string& filename, const FileContext& context) : originalFilename(filename) , resolvedFilename(FileOperations::getAbsolutePath( context.resolve(originalFilename))) { } const string& Filename::getOriginal() const { return originalFilename; } const string& Filename::getResolved() const { return resolvedFilename; } void Filename::updateAfterLoadState() { if (empty()) return; if (FileOperations::exists(resolvedFilename)) return; try { resolvedFilename = FileOperations::getAbsolutePath( UserFileContext().resolve(originalFilename)); } catch (MSXException&) { // nothing } } bool Filename::empty() const { assert(getOriginal().empty() == getResolved().empty()); return getOriginal().empty(); } void Filename::setResolved(const std::string& resolved) { resolvedFilename = resolved; } template void Filename::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("original", originalFilename); ar.serialize("resolved", resolvedFilename); } INSTANTIATE_SERIALIZE_METHODS(Filename); } // namespace openmsx openmsx-0.10.0/src/file/PreCacheFile.cc0000644000175000017500000000274312262345041020341 0ustar manuelmanuel00000000000000#include "PreCacheFile.hh" #include "FileOperations.hh" #include "statp.hh" #include #include namespace openmsx { PreCacheFile::PreCacheFile(const std::string& name_) : name(name_), thread(this), sem(1), exitLoop(false) { thread.start(); } PreCacheFile::~PreCacheFile() { { ScopedLock lock(sem); exitLoop = true; } thread.join(); } void PreCacheFile::run() { struct stat st; if (stat(name.c_str(), &st)) return; if (!S_ISREG(st.st_mode)) { // don't pre-cache non regular files (e.g. /dev/fd0) return; } FILE* file = FileOperations::openFile(name, "rb"); if (!file) return; fseek(file, 0, SEEK_END); auto size = ftell(file); if (size < 1024 * 1024) { // only pre-cache small files const unsigned BLOCK_SIZE = 4096; unsigned block = 0; unsigned repeat = 0; while (true) { bool exitLoop2; { ScopedLock lock(sem); exitLoop2 = exitLoop; } if (exitLoop2) break; char buf[BLOCK_SIZE]; fseek(file, block * BLOCK_SIZE, SEEK_SET); size_t read = fread(buf, 1, BLOCK_SIZE, file); if (read != BLOCK_SIZE) { // error or end-of-file reached, // in both cases stop pre-caching break; } // Just reading a file linearly from front to back // makes Linux classify the read as a 'streaming read'. // Linux doesn't cache those. To avoid this we read // some of the blocks twice. if (repeat != 0) { --repeat; ++block; } else { repeat = 5; } } } fclose(file); } } // namespace openmsx openmsx-0.10.0/src/file/ZlibInflate.cc0000644000175000017500000000446312262345041020273 0ustar manuelmanuel00000000000000#include "ZlibInflate.hh" #include "FileException.hh" #include "Math.hh" #include "MemBuffer.hh" #include "StringOp.hh" #include #include namespace openmsx { ZlibInflate::ZlibInflate(const byte* input, size_t inputLen_) { if (inputLen_ > std::numeric_limits::max()) { throw FileException( "Error while decompressing: input file too big"); } auto inputLen = static_cast(inputLen_); s.zalloc = nullptr; s.zfree = nullptr; s.opaque = nullptr; s.next_in = const_cast(input); s.avail_in = inputLen; wasInit = false; } ZlibInflate::~ZlibInflate() { if (wasInit) { inflateEnd(&s); } } void ZlibInflate::skip(size_t num) { for (size_t i = 0; i < num; ++i) { getByte(); } } byte ZlibInflate::getByte() { if (s.avail_in <= 0) { throw FileException( "Error while decompressing: unexpected end of file."); } --s.avail_in; return *(s.next_in++); } unsigned ZlibInflate::get16LE() { unsigned result = getByte(); result += getByte() << 8; return result; } unsigned ZlibInflate::get32LE() { unsigned result = getByte(); result += getByte() << 8; result += getByte() << 16; result += getByte() << 24; return result; } std::string ZlibInflate::getString(size_t len) { std::string result; for (size_t i = 0; i < len; ++i) { result.push_back(getByte()); } return result; } std::string ZlibInflate::getCString() { std::string result; while (char c = getByte()) { result.push_back(c); } return result; } void ZlibInflate::inflate(MemBuffer& output, size_t sizeHint) { int initErr = inflateInit2(&s, -MAX_WBITS); if (initErr != Z_OK) { throw FileException(StringOp::Builder() << "Error initializing inflate struct: " << zError(initErr)); } wasInit = true; output.resize(sizeHint); s.avail_out = uInt(output.size()); // TODO overflow? while (true) { s.next_out = output.data() + s.total_out; int err = ::inflate(&s, Z_NO_FLUSH); if (err == Z_STREAM_END) { break; } if (err != Z_OK) { throw FileException(StringOp::Builder() << "Error decompressing gzip: " << zError(err)); } auto oldSize = output.size(); output.resize(oldSize * 2); // double buffer size s.avail_out = uInt(output.size() - oldSize); // TODO overflow? } // set actual size output.resize(s.total_out); } } // namespace openmsx openmsx-0.10.0/src/file/ZipFileAdapter.hh0000644000175000017500000000053212262345041020736 0ustar manuelmanuel00000000000000#ifndef ZIPFILEADAPTER_HH #define ZIPFILEADAPTER_HH #include "CompressedFileAdapter.hh" namespace openmsx { class ZipFileAdapter : public CompressedFileAdapter { public: explicit ZipFileAdapter(std::unique_ptr file); private: virtual void decompress(FileBase& file, Decompressed& decompressed); }; } // namespace openmsx #endif openmsx-0.10.0/src/file/FileBase.cc0000644000175000017500000000230712262345041017535 0ustar manuelmanuel00000000000000#include "FileBase.hh" #include "FileOperations.hh" #include #include using std::string; namespace openmsx { FileBase::FileBase() { } FileBase::~FileBase() { munmap(); } const byte* FileBase::mmap(size_t& size) { if (mmapBuf.empty()) { size = getSize(); MemBuffer tmpBuf(size); read(tmpBuf.data(), size); std::swap(mmapBuf, tmpBuf); } return mmapBuf.data(); } void FileBase::munmap() { mmapBuf.clear(); } void FileBase::truncate(size_t newSize) { auto oldSize = getSize(); if (newSize < oldSize) { PRT_DEBUG("Default truncate() can't shrink file!"); return; } auto remaining = newSize - oldSize; seek(oldSize); static const size_t BUF_SIZE = 4096; byte buf[BUF_SIZE]; memset(buf, 0, sizeof(buf)); while (remaining) { auto chunkSize = std::min(BUF_SIZE, remaining); write(buf, chunkSize); remaining -= chunkSize; } } const string FileBase::getLocalReference() { // default implementation, file is not backed (uncompressed) on // the local file system return ""; } const string FileBase::getOriginalName() { // default implementation just returns filename portion of URL return FileOperations::getFilename(getURL()).str(); } } // namespace openmsx openmsx-0.10.0/src/file/LocalFile.cc0000644000175000017500000001436112262345041017720 0ustar manuelmanuel00000000000000#include "systemfuncs.hh" #include "unistdp.hh" #include #include #if HAVE_MMAP #include #endif #if defined _WIN32 #include #include #endif #include "LocalFile.hh" #include "FileOperations.hh" #include "FileException.hh" #include "FileNotFoundException.hh" #include "PreCacheFile.hh" #include "StringOp.hh" #include "memory.hh" #include // for strchr, strerror #include #include #ifndef EOVERFLOW #define EOVERFLOW 0 #endif using std::string; namespace openmsx { LocalFile::LocalFile(string_ref filename_, File::OpenMode mode) : filename(FileOperations::expandTilde(filename_)) #if HAVE_MMAP || defined _WIN32 , mmem(nullptr) #endif #if defined _WIN32 , hMmap(nullptr) #endif , readOnly(false) { PRT_DEBUG("LocalFile: " << filename); if (mode == File::SAVE_PERSISTENT) { auto pos = filename.find_last_of('/'); if (pos != string::npos) { FileOperations::mkdirp(filename.substr(0, pos)); } } const string name = FileOperations::getNativePath(filename); if ((mode == File::SAVE_PERSISTENT) || (mode == File::TRUNCATE)) { // open file read/write truncated file = FileOperations::openFile(name, "wb+"); } else if (mode == File::CREATE) { // open file read/write file = FileOperations::openFile(name, "rb+"); if (!file) { // create if it didn't exist yet file = FileOperations::openFile(name, "wb+"); } } else { // open file read/write file = FileOperations::openFile(name, "rb+"); if (!file) { // if that fails try read only file = FileOperations::openFile(name, "rb"); readOnly = true; } } if (!file) { int err = errno; if (err == ENOENT) { throw FileNotFoundException( "File \"" + filename + "\" not found"); } else { throw FileException( "Error opening file \"" + filename + "\": " + strerror(err)); } } getSize(); // check filesize } LocalFile::LocalFile(string_ref filename_, const char* mode) : filename(FileOperations::expandTilde(filename_)) #if HAVE_MMAP || defined _WIN32 , mmem(nullptr) #endif #if defined _WIN32 , hMmap(nullptr) #endif , readOnly(false) { assert(strchr(mode, 'b')); const string name = FileOperations::getNativePath(filename); file = FileOperations::openFile(name, mode); if (!file) { throw FileException("Error opening file \"" + filename + "\""); } getSize(); // check filesize } LocalFile::~LocalFile() { munmap(); fclose(file); } void LocalFile::preCacheFile() { string name = FileOperations::getNativePath(filename); cache = make_unique(name); } void LocalFile::read(void* buffer, size_t num) { if (fread(buffer, 1, num, file) != num) { if (ferror(file)) { throw FileException("Error reading file"); } if (feof(file)) { throw FileException("Read beyond end of file"); } } } void LocalFile::write(const void* buffer, size_t num) { if (fwrite(buffer, 1, num, file) != num) { if (ferror(file)) { throw FileException("Error writing file"); } } } #if defined _WIN32 const byte* LocalFile::mmap(size_t& size) { size = getSize(); if (size == 0) return nullptr; if (!mmem) { int fd = _fileno(file); if (fd == -1) { throw FileException("_fileno failed"); } auto hFile = reinterpret_cast(_get_osfhandle(fd)); // No need to close if (hFile == INVALID_HANDLE_VALUE) { throw FileException("_get_osfhandle failed"); } assert(!hMmap); hMmap = CreateFileMapping(hFile, nullptr, PAGE_WRITECOPY, 0, 0, nullptr); if (!hMmap) { throw FileException(StringOp::Builder() << "CreateFileMapping failed: " << GetLastError()); } mmem = static_cast(MapViewOfFile(hMmap, FILE_MAP_COPY, 0, 0, 0)); if (!mmem) { DWORD gle = GetLastError(); CloseHandle(hMmap); hMmap = nullptr; throw FileException(StringOp::Builder() << "MapViewOfFile failed: " << gle); } } return mmem; } void LocalFile::munmap() { if (mmem) { // TODO: make this a valid failure path // When pages are dirty, UnmapViewOfFile is a save operation, // and that can fail. However, mummap is called from // the destructor, for which there is no expectation // that it will fail. So this area needs some work. // It is NOT an option to throw an exception (not even // FatalError). if (!UnmapViewOfFile(mmem)) { std::cerr << "UnmapViewOfFile failed: " << StringOp::toString(GetLastError()) << std::endl; } mmem = nullptr; } if (hMmap) { CloseHandle(hMmap); hMmap = nullptr; } } #elif HAVE_MMAP const byte* LocalFile::mmap(size_t& size) { size = getSize(); if (size == 0) return nullptr; if (!mmem) { mmem = static_cast( ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fileno(file), 0)); // MAP_FAILED is #define'd using an old-style cast, we // have to redefine it ourselves to avoid a warning auto MY_MAP_FAILED = reinterpret_cast(-1); if (mmem == MY_MAP_FAILED) { throw FileException("Error mmapping file"); } } return mmem; } void LocalFile::munmap() { if (mmem) { ::munmap(const_cast(mmem), getSize()); mmem = nullptr; } } #endif size_t LocalFile::getSize() { struct stat st; int ret = fstat(fileno(file), &st); if (ret && (errno == EOVERFLOW)) { // on 32-bit systems, the fstat() call returns a EOVERFLOW // error in case the file is bigger than (1<<31)-1 bytes throw FileException("Files >= 2GB are not supported on " "32-bit platforms: " + getURL()); } if (ret) { throw FileException("Cannot get file size"); } return st.st_size; } void LocalFile::seek(size_t pos) { fseek(file, long(pos), SEEK_SET); if (ferror(file)) { throw FileException("Error seeking file"); } } size_t LocalFile::getPos() { return ftell(file); } #if HAVE_FTRUNCATE void LocalFile::truncate(size_t size) { int fd = fileno(file); if (ftruncate(fd, size)) { throw FileException("Error truncating file"); } } #endif void LocalFile::flush() { fflush(file); } const string LocalFile::getURL() const { return filename; } const string LocalFile::getLocalReference() { return filename; } bool LocalFile::isReadOnly() const { return readOnly; } time_t LocalFile::getModificationDate() { struct stat st; if (fstat(fileno(file), &st)) { throw FileException("Cannot stat file"); } return st.st_mtime; } } // namespace openmsx openmsx-0.10.0/src/file/CompressedFileAdapter.hh0000644000175000017500000000221112262345041022274 0ustar manuelmanuel00000000000000#ifndef COMPRESSEDFILEADAPTER_HH #define COMPRESSEDFILEADAPTER_HH #include "FileBase.hh" #include "MemBuffer.hh" #include namespace openmsx { class CompressedFileAdapter : public FileBase { public: struct Decompressed { MemBuffer buf; std::string originalName; std::string cachedURL; time_t cachedModificationDate; }; virtual void read(void* buffer, size_t num); virtual void write(const void* buffer, size_t num); virtual const byte* mmap(size_t& size); virtual void munmap(); virtual size_t getSize(); virtual void seek(size_t pos); virtual size_t getPos(); virtual void truncate(size_t size); virtual void flush(); virtual const std::string getURL() const; virtual const std::string getOriginalName(); virtual bool isReadOnly() const; virtual time_t getModificationDate(); protected: explicit CompressedFileAdapter(std::unique_ptr file); virtual ~CompressedFileAdapter(); virtual void decompress(FileBase& file, Decompressed& decompressed) = 0; private: void decompress(); std::unique_ptr file; std::shared_ptr decompressed; size_t pos; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/FilePool.cc0000644000175000017500000003145412262345041017601 0ustar manuelmanuel00000000000000#include "FilePool.hh" #include "File.hh" #include "FileException.hh" #include "FileContext.hh" #include "FileOperations.hh" #include "TclObject.hh" #include "StringSetting.hh" #include "ReadDir.hh" #include "Date.hh" #include "CommandController.hh" #include "CommandException.hh" #include "EventDistributor.hh" #include "CliComm.hh" #include "Timer.hh" #include "StringOp.hh" #include "memory.hh" #include "sha1.hh" #include "stl.hh" #include #include using std::ifstream; using std::get; using std::make_pair; using std::make_tuple; using std::ofstream; using std::pair; using std::string; using std::vector; using std::unique_ptr; namespace openmsx { const char* const FILE_CACHE = "/.filecache"; static string initialFilePoolSettingValue() { TclObject result; for (auto& p : SystemFileContext().getPaths()) { TclObject entry1; entry1.addListElement("-path"); entry1.addListElement(FileOperations::join(p, "systemroms")); entry1.addListElement("-types"); entry1.addListElement("system_rom"); result.addListElement(entry1); TclObject entry2; entry2.addListElement("-path"); entry2.addListElement(FileOperations::join(p, "software")); entry2.addListElement("-types"); entry2.addListElement("rom disk tape"); result.addListElement(entry2); } return result.getString().str(); } FilePool::FilePool(CommandController& controller, EventDistributor& distributor_) : filePoolSetting(make_unique( controller, "__filepool", "This is an internal setting. Don't change this directly, " "instead use the 'filepool' command.", initialFilePoolSettingValue())) , distributor(distributor_) , cliComm(controller.getCliComm()) , quit(false) { filePoolSetting->attach(*this); distributor.registerEventListener(OPENMSX_QUIT_EVENT, *this); readSha1sums(); needWrite = false; } FilePool::~FilePool() { if (needWrite) { writeSha1sums(); } distributor.unregisterEventListener(OPENMSX_QUIT_EVENT, *this); filePoolSetting->detach(*this); } void FilePool::insert(const Sha1Sum& sum, time_t time, const string& filename) { auto it = upper_bound(pool.begin(), pool.end(), sum, LessTupleElement<0>()); pool.insert(it, make_tuple(sum, time, filename)); needWrite = true; } void FilePool::remove(Pool::iterator it) { pool.erase(it); needWrite = true; } // Change the sha1sum of the element pointed to by 'it' into 'newSum'. // Also re-arrange the items so that pool remains sorted on sha1sum. Internally // this method doesn't actually sort, it merely rotates the elements. // Returns false if the new position is before (or at) the old position. // Returns true if the new position is after the old position. bool FilePool::adjust(Pool::iterator it, const Sha1Sum& newSum) { needWrite = true; auto newIt = upper_bound(pool.begin(), pool.end(), newSum, LessTupleElement<0>()); get<0>(*it) = newSum; // update sum if (newIt > it) { // move to back rotate(it, it + 1, newIt); return true; } else { if (newIt < it) { // move to front rotate(newIt, it, it + 1); } else { // (unlikely) sha1sum has changed, but after // resorting item would remain in the same // position } return false; } } static bool parse(const string& line, Sha1Sum& sha1, time_t& time, string& filename) { if (line.size() <= 68) return false; try { sha1.parse40(line.data()); } catch (MSXException& /*e*/) { return false; } time = Date::fromString(line.data() + 42); if (time == time_t(-1)) return false; filename.assign(line, 68, line.size()); return true; } void FilePool::readSha1sums() { assert(pool.empty()); string cacheFile = FileOperations::getUserDataDir() + FILE_CACHE; ifstream file(cacheFile.c_str()); string line; Sha1Sum sum; string filename; time_t time; while (file.good()) { getline(file, line); if (parse(line, sum, time, filename)) { pool.push_back(make_tuple(sum, time, filename)); } } if (!std::is_sorted(pool.begin(), pool.end(), LessTupleElement<0>())) { // This should _rarely_ happen. In fact it should only happen // when .filecache was manually edited. Though because it's // very important that pool is indeed sorted I've added this // safety mechanism. sort(pool.begin(), pool.end(), LessTupleElement<0>()); } } void FilePool::writeSha1sums() { string cacheFile = FileOperations::getUserDataDir() + FILE_CACHE; ofstream file; FileOperations::openofstream(file, cacheFile); if (!file.is_open()) { return; } for (auto& p : pool) { file << get<0>(p).toString() << " " // sum << Date::toString(get<1>(p)) << " " // date << get<2>(p) // filename << '\n'; } } static int parseTypes(const TclObject& list) { int result = 0; unsigned num = list.getListLength(); for (unsigned i = 0; i < num; ++i) { string_ref elem = list.getListIndex(i).getString(); if (elem == "system_rom") { result |= FilePool::SYSTEM_ROM; } else if (elem == "rom") { result |= FilePool::ROM; } else if (elem == "disk") { result |= FilePool::DISK; } else if (elem == "tape") { result |= FilePool::TAPE; } else { throw CommandException("Unknown type: " + elem); } } return result; } void FilePool::update(const Setting& setting) { assert(&setting == filePoolSetting.get()); (void)setting; getDirectories(); // check for syntax errors } FilePool::Directories FilePool::getDirectories() const { Directories result; const TclObject& all = filePoolSetting->getValue(); unsigned numLines = all.getListLength(); for (unsigned i = 0; i < numLines; ++i) { Entry entry; bool hasPath = false; entry.types = 0; TclObject line = all.getListIndex(i); unsigned numItems = line.getListLength(); if (numItems & 1) { throw CommandException( "Expected a list with an even number " "of elements, but got " + line.getString()); } for (unsigned j = 0; j < numItems; j += 2) { string_ref name = line.getListIndex(j + 0).getString(); TclObject value = line.getListIndex(j + 1); if (name == "-path") { entry.path = value.getString().str(); hasPath = true; } else if (name == "-types") { entry.types = parseTypes(value); } else { throw CommandException( "Unknown item: " + name); } } if (!hasPath) { throw CommandException( "Missing -path item: " + line.getString()); } if (entry.types == 0) { throw CommandException( "Missing -types item: " + line.getString()); } result.push_back(entry); } return result; } unique_ptr FilePool::getFile(FileType fileType, const Sha1Sum& sha1sum) { unique_ptr result; result = getFromPool(sha1sum); if (result) return result; // not found in cache, need to scan directories lastTime = Timer::getTime(); // for progress messages amountScanned = 0; // also for progress messages Directories directories; try { directories = getDirectories(); } catch (CommandException& e) { cliComm.printWarning("Error while parsing '__filepool' setting" + e.getMessage()); } for (auto& d : directories) { if (d.types & fileType) { string path = FileOperations::expandTilde(d.path); result = scanDirectory(sha1sum, path, d.path); if (result) return result; } } return result; // not found } static Sha1Sum calcSha1sum(File& file, CliComm& cliComm, EventDistributor& distributor) { size_t size; const byte* data = file.mmap(size); return SHA1::calcWithProgress(data, size, file.getOriginalName(), cliComm, distributor); } unique_ptr FilePool::getFromPool(const Sha1Sum& sha1sum) { auto bound = equal_range(pool.begin(), pool.end(), sha1sum, LessTupleElement<0>()); // use indices instead of iterators auto i = distance(pool.begin(), bound.first); auto last = distance(pool.begin(), bound.second); while (i != last) { auto it = pool.begin() + i; auto& time = get<1>(*it); const auto& filename = get<2>(*it); try { auto file = make_unique(filename); auto newTime = file->getModificationDate(); if (time == newTime) { // When modification time is unchanged, assume // sha1sum is also unchanged. So avoid // expensive sha1sum calculation. return file; } time = newTime; // update timestamp needWrite = true; auto newSum = calcSha1sum(*file, cliComm, distributor); if (newSum == sha1sum) { // Modification time was changed, but // (recalculated) sha1sum is still the same. return file; } // Sha1sum has changed: update sha1sum, move entry to // new position new sum and continue searching. if (adjust(it, newSum)) { // after --last; // no ++i } else { // before (or at) ++i; } } catch (FileException&) { // Error reading file: remove from db and continue // searching. remove(it); --last; } } return nullptr; // not found } unique_ptr FilePool::scanDirectory(const Sha1Sum& sha1sum, const string& directory, const string& poolPath) { ReadDir dir(directory); while (dirent* d = dir.getEntry()) { if (quit) { // Scanning can take a long time. Allow to exit // openmsx when it takes too long. Stop scanning // by pretending we didn't find the file. return nullptr; } string file = d->d_name; string path = directory + '/' + file; FileOperations::Stat st; if (FileOperations::getStat(path, st)) { unique_ptr result; if (FileOperations::isRegularFile(st)) { result = scanFile(sha1sum, path, st, poolPath); } else if (FileOperations::isDirectory(st)) { if ((file != ".") && (file != "..")) { result = scanDirectory(sha1sum, path, poolPath); } } if (result) return result; } } return nullptr; // not found } unique_ptr FilePool::scanFile(const Sha1Sum& sha1sum, const string& filename, const FileOperations::Stat& st, const string& poolPath) { amountScanned++; // Periodically send a progress message with the current filename auto now = Timer::getTime(); if (now > (lastTime + 250000)) { // 4Hz lastTime = now; cliComm.printProgress("Searching for file with sha1sum " + sha1sum.toString() + "...\nIndexing filepool " + poolPath + ": [" + StringOp::toString(amountScanned) + "]: " + filename.substr(poolPath.size())); } // deliverEvents() is relatively cheap when there are no events to // deliver, so it's ok to call on each file. distributor.deliverEvents(); auto it = findInDatabase(filename); if (it == pool.end()) { // not in pool try { auto file = make_unique(filename); auto sum = calcSha1sum(*file, cliComm, distributor); auto time = FileOperations::getModificationDate(st); insert(sum, time, filename); if (sum == sha1sum) { return file; } } catch (FileException&) { // ignore } } else { // already in pool assert(filename == get<2>(*it)); try { auto time = FileOperations::getModificationDate(st); if (time == get<1>(*it)) { // db is still up to date if (get<0>(*it) == sha1sum) { return make_unique(filename); } } else { // db outdated auto file = make_unique(filename); auto sum = calcSha1sum(*file, cliComm, distributor); get<1>(*it) = time; adjust(it, sum); if (sum == sha1sum) { return file; } } } catch (FileException&) { // error reading file, remove from db remove(it); } } return nullptr; // not found } FilePool::Pool::iterator FilePool::findInDatabase(const string& filename) { // Linear search in pool for filename. // Search from back to front because often, soon after this search, we // will insert/remove an element from the vector. This requires // shifting all elements in the vector starting from a certain // position. Starting the search from the back increases the likelihood // that the to-be-shifted elements are already in the memory cache. for (auto it = pool.rbegin(); it != pool.rend(); ++it) { if (get<2>(*it) == filename) { return it.base() - 1; } } return pool.end(); // not found } Sha1Sum FilePool::getSha1Sum(File& file) { auto time = file.getModificationDate(); const auto& filename = file.getURL(); auto it = findInDatabase(filename); if ((it != pool.end()) && (get<1>(*it) == time)) { // in database and modification time matches, // assume sha1sum also matches return get<0>(*it); } // not in database or timestamp mismatch auto sum = calcSha1sum(file, cliComm, distributor); if (it == pool.end()) { // was not yet in database, insert new entry insert(sum, time, filename); } else { // was already in database, but with wrong timestamp (and sha1sum) get<1>(*it) = time; adjust(it, sum); } return sum; } void FilePool::removeSha1Sum(File& file) { auto it = findInDatabase(file.getURL()); if (it != pool.end()) { remove(it); } } int FilePool::signalEvent(const std::shared_ptr& event) { (void)event; // avoid warning for non-assert compiles assert(event->getType() == OPENMSX_QUIT_EVENT); quit = true; return 0; } } // namespace openmsx openmsx-0.10.0/src/file/CompressedFileAdapter.cc0000644000175000017500000000522612262345041022273 0ustar manuelmanuel00000000000000#include "CompressedFileAdapter.hh" #include "FileException.hh" #include "StringMap.hh" #include #include #include using std::string; namespace openmsx { static StringMap> decompressCache; CompressedFileAdapter::CompressedFileAdapter(std::unique_ptr file_) : file(std::move(file_)), pos(0) { } CompressedFileAdapter::~CompressedFileAdapter() { auto it = decompressCache.find(getURL()); decompressed.reset(); if (it != decompressCache.end() && it->second.unique()) { // delete last user of Decompressed, remove from cache decompressCache.erase(it); } } void CompressedFileAdapter::decompress() { if (decompressed) return; string url = getURL(); auto it = decompressCache.find(url); if (it != decompressCache.end()) { decompressed = it->second; } else { decompressed = std::make_shared(); decompress(*file, *decompressed); decompressed->cachedModificationDate = getModificationDate(); decompressed->cachedURL = url; decompressCache[url] = decompressed; } // close original file after succesful decompress file.reset(); } void CompressedFileAdapter::read(void* buffer, size_t num) { decompress(); const MemBuffer& buf = decompressed->buf; if (buf.size() < (pos + num)) { throw FileException("Read beyond end of file"); } memcpy(buffer, buf.data() + pos, num); pos += num; } void CompressedFileAdapter::write(const void* /*buffer*/, size_t /*num*/) { throw FileException("Writing to compressed files not yet supported"); } const byte* CompressedFileAdapter::mmap(size_t& size) { decompress(); size = decompressed->buf.size(); return reinterpret_cast(decompressed->buf.data()); } void CompressedFileAdapter::munmap() { // nothing } size_t CompressedFileAdapter::getSize() { decompress(); return decompressed->buf.size(); } void CompressedFileAdapter::seek(size_t newpos) { pos = newpos; } size_t CompressedFileAdapter::getPos() { return pos; } void CompressedFileAdapter::truncate(size_t /*size*/) { throw FileException("Truncating compressed files not yet supported."); } void CompressedFileAdapter::flush() { // nothing because writing is not supported } const string CompressedFileAdapter::getURL() const { return file ? file->getURL() : decompressed->cachedURL; } const string CompressedFileAdapter::getOriginalName() { decompress(); return decompressed->originalName; } bool CompressedFileAdapter::isReadOnly() const { return true; } time_t CompressedFileAdapter::getModificationDate() { return file ? file->getModificationDate() : decompressed->cachedModificationDate; } } // namespace openmsx openmsx-0.10.0/src/file/ReadDir.cc0000644000175000017500000000061012262345041017370 0ustar manuelmanuel00000000000000#include "ReadDir.hh" namespace openmsx { ReadDir::ReadDir(const std::string& directory) { dir = opendir(directory.empty() ? "." : directory.c_str()); } ReadDir::~ReadDir() { if (dir) { closedir(dir); } } struct dirent* ReadDir::getEntry() { if (!dir) { return nullptr; } return readdir(dir); } bool ReadDir::isValid() const { return dir != nullptr; } } // namespace openmsx openmsx-0.10.0/src/file/GZFileAdapter.cc0000644000175000017500000000317612262345041020511 0ustar manuelmanuel00000000000000#include "GZFileAdapter.hh" #include "ZlibInflate.hh" #include "FileException.hh" namespace openmsx { const byte ASCII_FLAG = 0x01; // bit 0 set: file probably ascii text const byte HEAD_CRC = 0x02; // bit 1 set: header CRC present const byte EXTRA_FIELD = 0x04; // bit 2 set: extra field present const byte ORIG_NAME = 0x08; // bit 3 set: original file name present const byte COMMENT = 0x10; // bit 4 set: file comment present const byte RESERVED = 0xE0; // bits 5..7: reserved GZFileAdapter::GZFileAdapter(std::unique_ptr file_) : CompressedFileAdapter(std::move(file_)) { } static bool skipHeader(ZlibInflate& zlib, std::string& originalName) { // check magic bytes if (zlib.get16LE() != 0x8B1F) { return false; } byte method = zlib.getByte(); byte flags = zlib.getByte(); if (method != Z_DEFLATED || (flags & RESERVED) != 0) { return false; } // Discard time, xflags and OS code: zlib.skip(6); if ((flags & EXTRA_FIELD) != 0) { // skip the extra field int len = zlib.get16LE(); zlib.skip(len); } if ((flags & ORIG_NAME) != 0) { // get the original file name originalName = zlib.getCString(); } if ((flags & COMMENT) != 0) { // skip the .gz file comment zlib.getCString(); } if ((flags & HEAD_CRC) != 0) { // skip the header crc zlib.skip(2); } return true; } void GZFileAdapter::decompress(FileBase& file, Decompressed& decompressed) { size_t size; const byte* data = file.mmap(size); ZlibInflate zlib(data, size); if (!skipHeader(zlib, decompressed.originalName)) { throw FileException("Not a gzip header"); } zlib.inflate(decompressed.buf); } } // namespace openmsx openmsx-0.10.0/src/file/FileBase.hh0000644000175000017500000000166012262345041017550 0ustar manuelmanuel00000000000000#ifndef FILEBASE_HH #define FILEBASE_HH #include "MemBuffer.hh" #include "openmsx.hh" #include "noncopyable.hh" #include namespace openmsx { class FileBase : private noncopyable { public: FileBase(); virtual ~FileBase(); virtual void read(void* buffer, size_t num) = 0; virtual void write(const void* buffer, size_t num) = 0; // If you override mmap(), make sure to call munmap() in // your destructor. virtual const byte* mmap(size_t& size); virtual void munmap(); virtual size_t getSize() = 0; virtual void seek(size_t pos) = 0; virtual size_t getPos() = 0; virtual void truncate(size_t size); virtual void flush() = 0; virtual const std::string getURL() const = 0; virtual const std::string getLocalReference(); virtual const std::string getOriginalName(); virtual bool isReadOnly() const = 0; virtual time_t getModificationDate() = 0; private: MemBuffer mmapBuf; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/FileException.hh0000644000175000017500000000040412262345041020627 0ustar manuelmanuel00000000000000#ifndef FILEEXCEPTION_HH #define FILEEXCEPTION_HH #include "MSXException.hh" namespace openmsx { class FileException : public MSXException { public: explicit FileException(string_ref message) : MSXException(message) {} }; } // namespace openmsx #endif openmsx-0.10.0/src/file/FileOperations.cc0000644000175000017500000004541512262345041021015 0ustar manuelmanuel00000000000000#ifdef _WIN32 #ifndef _WIN32_IE #define _WIN32_IE 0x0500 // For SHGetSpecialFolderPathW with MinGW #endif #include "utf8_checked.hh" #include "vla.hh" #include #include #include #include #include #include #include #include #include #else // ifdef _WIN32_ ... #include #include #include #include #endif // ifdef _WIN32_ ... else ... #include "systemfuncs.hh" #if HAVE_NFTW #include #endif #if defined(PATH_MAX) #define MAXPATHLEN PATH_MAX #elif defined(MAX_PATH) #define MAXPATHLEN MAX_PATH #else #define MAXPATHLEN 4096 #endif #ifdef __APPLE__ #include #endif #include "openmsx.hh" #include "ReadDir.hh" #include "FileOperations.hh" #include "FileException.hh" #include "StringOp.hh" #include "statp.hh" #include "unistdp.hh" #include "countof.hh" #include "build-info.hh" #include "AndroidApiWrapper.hh" #include #include #include #include #ifndef _MSC_VER #include #endif using std::string; #ifdef _WIN32 using namespace utf8; #endif namespace openmsx { namespace FileOperations { #ifdef __APPLE__ static std::string findShareDir() { // Find bundle location: // for an app folder, this is the outer directory, // for an unbundled executable, it is the executable itself. ProcessSerialNumber psn; if (GetCurrentProcess(&psn) != noErr) { throw FatalError("Failed to get process serial number"); } FSRef location; if (GetProcessBundleLocation(&psn, &location) != noErr) { throw FatalError("Failed to get process bundle location"); } // Get info about the location. FSCatalogInfo catalogInfo; FSRef parentRef; if (FSGetCatalogInfo( &location, kFSCatInfoVolume | kFSCatInfoNodeFlags, &catalogInfo, nullptr, nullptr, &parentRef ) != noErr) { throw FatalError("Failed to get info about bundle path"); } // Get reference to root directory of the volume we are searching. // We will need this later to know when to give up. FSRef root; if (FSGetVolumeInfo( catalogInfo.volume, 0, nullptr, kFSVolInfoNone, nullptr, nullptr, &root ) != noErr) { throw FatalError("Failed to get reference to root directory"); } // Make sure we are looking at a directory. if (~catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) { // Location is not a directory, so it is the path to the executable. location = parentRef; } while (true) { // Iterate through the files in the directory. FSIterator iterator; if (FSOpenIterator(&location, kFSIterateFlat, &iterator) != noErr) { throw FatalError("Failed to open iterator"); } bool filesLeft = true; // iterator has files left for next call while (filesLeft) { // Get info about several files at a time. const int MAX_SCANNED_FILES = 100; ItemCount actualObjects; FSRef refs[MAX_SCANNED_FILES]; FSCatalogInfo catalogInfos[MAX_SCANNED_FILES]; HFSUniStr255 names[MAX_SCANNED_FILES]; OSErr err = FSGetCatalogInfoBulk( iterator, MAX_SCANNED_FILES, &actualObjects, nullptr /*containerChanged*/, kFSCatInfoNodeFlags, catalogInfos, refs, nullptr /*specs*/, names ); if (err == errFSNoMoreItems) { filesLeft = false; } else if (err != noErr) { throw FatalError("Catalog get failed"); } for (ItemCount i = 0; i < actualObjects; i++) { // We're only interested in subdirectories. if (catalogInfos[i].nodeFlags & kFSNodeIsDirectoryMask) { // Convert the name to a CFString. CFStringRef name = CFStringCreateWithCharactersNoCopy( kCFAllocatorDefault, names[i].unicode, names[i].length, kCFAllocatorNull // do not deallocate character buffer ); // Is this the directory we are looking for? static const CFStringRef SHARE = CFSTR("share"); CFComparisonResult cmp = CFStringCompare(SHARE, name, 0); CFRelease(name); if (cmp == kCFCompareEqualTo) { // Clean up. OSErr closeErr = FSCloseIterator(iterator); assert(closeErr == noErr); (void)closeErr; // Get full path of directory. UInt8 path[256]; if (FSRefMakePath( &refs[i], path, sizeof(path)) != noErr ) { throw FatalError("Path too long"); } return std::string(reinterpret_cast(path)); } } } } OSErr closeErr = FSCloseIterator(iterator); assert(closeErr == noErr); (void)closeErr; // Are we in the root yet? if (FSCompareFSRefs(&location, &root) == noErr) { throw FatalError("Could not find \"share\" directory anywhere"); } // Go up one level. if (FSGetCatalogInfo( &location, kFSCatInfoNone, nullptr, nullptr, nullptr, &parentRef ) != noErr ) { throw FatalError("Failed to get parent directory"); } location = parentRef; } } #endif // __APPLE__ string expandTilde(string_ref path) { if (path.empty() || path[0] != '~') { return path.str(); } auto pos = path.find_first_of('/'); string_ref user = ((path.size() == 1) || (pos == 1)) ? "" : path.substr(1, (pos == string_ref::npos) ? pos : pos - 1); string result = getUserHomeDir(user); if (result.empty()) { // failed to find homedir, return the path unchanged return path.str(); } if (pos == string_ref::npos) { return result; } if (result.back() != '/') { result += '/'; } string_ref last = path.substr(pos + 1); result.append(last.data(), last.size()); return result; } void mkdir(const string& path, mode_t mode) { #ifdef _WIN32 (void)&mode; // Suppress C4100 VC++ warning if ((path == "/") || StringOp::endsWith(path, ':') || StringOp::endsWith(path, ":/")) { return; } int result = _wmkdir(utf8to16(getNativePath(path)).c_str()); #else int result = ::mkdir(path.c_str(), mode); #endif if (result && (errno != EEXIST)) { throw FileException("Error creating dir " + path); } } void mkdirp(string_ref path_) { if (path_.empty()) { return; } string path = expandTilde(path_); string::size_type pos = 0; do { pos = path.find_first_of('/', pos + 1); mkdir(path.substr(0, pos), 0755); } while (pos != string::npos); if (!isDirectory(path)) { throw FileException("Error creating dir " + path); } } int unlink(const std::string& path) { #ifdef _WIN32 return _wunlink(utf8to16(path).c_str()); #else return ::unlink(path.c_str()); #endif } int rmdir(const std::string& path) { #ifdef _WIN32 return _wrmdir(utf8to16(path).c_str()); #else return ::rmdir(path.c_str()); #endif } #ifdef _WIN32 int deleteRecursive(const std::string& path) { std::wstring pathW = utf8to16(path); SHFILEOPSTRUCTW rmdirFileop; rmdirFileop.hwnd = nullptr; rmdirFileop.wFunc = FO_DELETE; rmdirFileop.pFrom = pathW.c_str(); rmdirFileop.pTo = nullptr; rmdirFileop.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI; rmdirFileop.fAnyOperationsAborted = FALSE; rmdirFileop.hNameMappings = nullptr; rmdirFileop.lpszProgressTitle = nullptr; return SHFileOperationW(&rmdirFileop); } #elif HAVE_NFTW int deleteRecursive_cb(const char* fpath, const struct stat* /*sb*/, int /*typeflag*/, struct FTW* /*ftwbuf*/) { return remove(fpath); } int deleteRecursive(const std::string& path) { return nftw(path.c_str(), deleteRecursive_cb, 64, FTW_DEPTH | FTW_PHYS); } #else // This is a platform independent version of deleteRecursive() (it builds on // top of helper routines that _are_ platform specific). Though I still prefer // the two platform specific deleteRecursive() routines above because they are // likely more optimized and likely contain less bugs than this version (e.g. // we're walking over the entries in a directory while simultaneously deleting // entries in that same directory. Although this seems to work fine, I'm not // 100% sure our ReadDir 'emulation code' for windows covers all corner cases. // While the windows version above very likely does handle everything). int deleteRecursive(const std::string& path) { if (isDirectory(path)) { { ReadDir dir(path); while (dirent* d = dir.getEntry()) { int err = deleteRecursive(d->d_name); if (err) return err; } } return rmdir(path); } else { return unlink(path); } } #endif FILE* openFile(const std::string& filename, const std::string& mode) { // Mode must contain a 'b' character. On unix this doesn't make any // difference. But on windows this is required to open the file // in binary mode. assert(mode.find('b') != std::string::npos); #ifdef _WIN32 return _wfopen( utf8to16(filename).c_str(), utf8to16(mode).c_str()); #else return fopen(filename.c_str(), mode.c_str()); #endif } void openofstream(std::ofstream& stream, const std::string& filename) { #if defined _WIN32 && defined _MSC_VER // MinGW 3.x doesn't support ofstream.open(wchar_t*) // TODO - this means that unicode text may not work right here stream.open(utf8to16(filename).c_str()); #else stream.open(filename.c_str()); #endif } void openofstream(std::ofstream& stream, const std::string& filename, std::ios_base::openmode mode) { #if defined _WIN32 && defined _MSC_VER // MinGW 3.x doesn't support ofstream.open(wchar_t*) // TODO - this means that unicode text may not work right here stream.open(utf8to16(filename).c_str(), mode); #else stream.open(filename.c_str(), mode); #endif } string_ref getFilename(string_ref path) { auto pos = path.rfind('/'); if (pos == string_ref::npos) { return path; } else { return path.substr(pos + 1); } } string_ref getBaseName(string_ref path) { auto pos = path.rfind('/'); if (pos == string_ref::npos) { return ""; } else { return path.substr(0, pos + 1); } } string_ref getExtension(string_ref path) { string_ref filename = getFilename(path); auto pos = filename.rfind('.'); if (pos == string_ref::npos) { return ""; } else { return filename.substr(pos + 1); } } string_ref stripExtension(string_ref path) { auto pos = path.rfind('.'); if (pos == string_ref::npos) { return path; } else { return path.substr(0, pos); } } string join(string_ref part1, string_ref part2) { if (part1.empty() || isAbsolutePath(part2)) { return part2.str(); } if (part1.back() == '/') { return part1 + part2; } return part1 + '/' + part2; } string join(string_ref part1, string_ref part2, string_ref part3) { return join(part1, join(part2, part3)); } string join(string_ref part1, string_ref part2, string_ref part3, string_ref part4) { return join(part1, join(part2, join(part3, part4))); } string getNativePath(string_ref path) { string result = path.str(); #ifdef _WIN32 replace(result.begin(), result.end(), '/', '\\'); #endif return result; } string getConventionalPath(string_ref path) { string result = path.str(); #ifdef _WIN32 replace(result.begin(), result.end(), '\\', '/'); #endif return result; } string getCurrentWorkingDirectory() { #ifdef _WIN32 wchar_t bufW[MAXPATHLEN]; wchar_t* result = _wgetcwd(bufW, MAXPATHLEN); string buf; if (result) { buf = utf16to8(result); } #else char buf[MAXPATHLEN]; char* result = getcwd(buf, MAXPATHLEN); #endif if (!result) { throw FileException("Couldn't get current working directory."); } return buf; } string getAbsolutePath(string_ref path) { // In rare cases getCurrentWorkingDirectory() can throw, // so only call it when really necessary. if (isAbsolutePath(path)) { return path.str(); } string currentDir = getCurrentWorkingDirectory(); return join(currentDir, path); } bool isAbsolutePath(string_ref path) { #ifdef _WIN32 if ((path.size() >= 3) && (path[1] == ':') && (path[2] == '/')) { char drive = tolower(path[0]); if (('a' <= drive) && (drive <= 'z')) { return true; } } #endif return !path.empty() && (path[0] == '/'); } string getUserHomeDir(string_ref username) { #ifdef _WIN32 (void)(&username); // ignore parameter, avoid warning wchar_t bufW[MAXPATHLEN + 1]; if (!SHGetSpecialFolderPathW(nullptr, bufW, CSIDL_PERSONAL, TRUE)) { throw FatalError(StringOp::Builder() << "SHGetSpecialFolderPathW failed: " << GetLastError()); } return getConventionalPath(utf16to8(bufW)); #else const char* dir = nullptr; struct passwd* pw = nullptr; if (username.empty()) { dir = getenv("HOME"); if (!dir) { pw = getpwuid(getuid()); } } else { pw = getpwnam(username.str().c_str()); } if (pw) { dir = pw->pw_dir; } return dir ? dir : ""; #endif } const string& getUserOpenMSXDir() { #ifdef _WIN32 static const string OPENMSX_DIR = expandTilde("~/openMSX"); #elif PLATFORM_ANDROID static const string OPENMSX_DIR = AndroidApiWrapper::getStorageDirectory() + "/openMSX"; #else static const string OPENMSX_DIR = expandTilde("~/.openMSX"); #endif return OPENMSX_DIR; } string getUserDataDir() { const char* const NAME = "OPENMSX_USER_DATA"; char* value = getenv(NAME); return value ? value : getUserOpenMSXDir() + "/share"; } string getSystemDataDir() { const char* const NAME = "OPENMSX_SYSTEM_DATA"; if (char* value = getenv(NAME)) { return value; } string newValue; #ifdef _WIN32 wchar_t bufW[MAXPATHLEN + 1]; int res = GetModuleFileNameW(nullptr, bufW, countof(bufW)); if (!res) { throw FatalError(StringOp::Builder() << "Cannot detect openMSX directory. GetModuleFileNameW failed: " << GetLastError()); } string filename = utf16to8(bufW); auto pos = filename.find_last_of('\\'); if (pos == string::npos) { throw FatalError("openMSX is not in directory!?"); } newValue = getConventionalPath(filename.substr(0, pos)) + "/share"; #elif defined(__APPLE__) newValue = findShareDir(); #elif PLATFORM_ANDROID newValue = getAbsolutePath("openmsx_system"); ad_printf("System data dir: %s", newValue.c_str()); #else // defined in build-info.hh (default /opt/openMSX/share) newValue = DATADIR; #endif return newValue; } #ifdef _WIN32 bool driveExists(char driveLetter) { char buf[] = { driveLetter, ':', 0 }; return GetFileAttributesA(buf) != INVALID_FILE_ATTRIBUTES; } #endif string expandCurrentDirFromDrive(string_ref path) { string result = path.str(); #ifdef _WIN32 if (((path.size() == 2) && (path[1] == ':')) || ((path.size() >= 3) && (path[1] == ':') && (path[2] != '/'))) { // get current directory for this drive unsigned char drive = tolower(path[0]); if (('a' <= drive) && (drive <= 'z')) { wchar_t bufW[MAXPATHLEN + 1]; if (driveExists(drive) && _wgetdcwd(drive - 'a' + 1, bufW, MAXPATHLEN)) { result = getConventionalPath(utf16to8(bufW)); if (result.back() != '/') { result += '/'; } if (path.size() > 2) { string_ref tmp = path.substr(2); result.append(tmp.data(), tmp.size()); } } } } #endif return result; } bool getStat(const string& filename_, Stat& st) { string filename = expandTilde(filename_); // workaround for VC++: strip trailing slashes (but keep it if it's the // only character in the path) auto pos = filename.find_last_not_of('/'); if (pos == string::npos) { // string was either empty or a (sequence of) '/' character(s) filename = filename.empty() ? "" : "/"; } else { filename.resize(pos + 1); } #ifdef _WIN32 return _wstat(utf8to16(filename).c_str(), &st) == 0; #else return stat(filename.c_str(), &st) == 0; #endif } bool isRegularFile(const Stat& st) { return S_ISREG(st.st_mode); } bool isRegularFile(const string& filename) { Stat st; return getStat(filename, st) && isRegularFile(st); } bool isDirectory(const Stat& st) { return S_ISDIR(st.st_mode); } bool isDirectory(const string& directory) { Stat st; return getStat(directory, st) && isDirectory(st); } bool exists(const string& filename) { Stat st; // dummy return getStat(filename, st); } time_t getModificationDate(const Stat& st) { return st.st_mtime; } static int getNextNum(dirent* d, string_ref prefix, string_ref extension, unsigned nofdigits) { auto extensionLen = extension.size(); auto prefixLen = prefix.size(); string_ref name(d->d_name); if ((name.size() != (prefixLen + nofdigits + extensionLen)) || (name.substr(0, prefixLen) != prefix) || (name.substr(prefixLen + nofdigits, extensionLen) != extension)) { return 0; } string_ref num = name.substr(prefixLen, nofdigits); string_ref::size_type idx; unsigned long n = stoul(num, &idx, 10); return (idx == num.size()) ? n : 0; } string getNextNumberedFileName( string_ref directory, string_ref prefix, string_ref extension) { const unsigned nofdigits = 4; int max_num = 0; string dirName = getUserOpenMSXDir() + '/' + directory; try { mkdirp(dirName); } catch (FileException&) { // ignore } ReadDir dir(dirName); while (dirent* d = dir.getEntry()) { max_num = std::max(max_num, getNextNum(d, prefix, extension, nofdigits)); } std::ostringstream os; os << dirName << '/' << prefix; os.width(nofdigits); os.fill('0'); os << (max_num + 1) << extension; return os.str(); } string parseCommandFileArgument( string_ref argument, string_ref directory, string_ref prefix, string_ref extension) { if (argument.empty()) { // directory is also created when needed return getNextNumberedFileName(directory, prefix, extension); } string filename = argument.str(); if (getBaseName(filename).empty()) { // no dir given, use standard dir (and create it) string dir = getUserOpenMSXDir() + '/' + directory; mkdirp(dir); filename = dir + '/' + filename; } else { filename = expandTilde(filename); } if (!StringOp::endsWith(filename, extension) && !exists(filename)) { // Expected extension not already given, append it. But only // when the filename without extension doesn't already exist. // Without this exception stuff like 'soundlog start /dev/null' // reports an error " ... error opening file /dev/null.wav." filename.append(extension.data(), extension.size()); } return filename; } string getTempDir() { #ifdef _WIN32 DWORD len = GetTempPathW(0, nullptr); if (len) { VLA(wchar_t, bufW, (len+1)); len = GetTempPathW(len, bufW); if (len) { // Strip last backslash if (bufW[len-1] == L'\\') { bufW[len-1] = L'\0'; } return utf16to8(bufW); } } throw FatalError(StringOp::Builder() << "GetTempPathW failed: " << GetLastError()); #elif PLATFORM_ANDROID string result = getSystemDataDir() + "/tmp"; return result; #else const char* result = nullptr; if (!result) result = getenv("TMPDIR"); if (!result) result = getenv("TMP"); if (!result) result = getenv("TEMP"); if (!result) { result = "/tmp"; } return result; #endif } FILE* openUniqueFile(const std::string& directory, std::string& filename) { #ifdef _WIN32 std::wstring directoryW = utf8to16(directory); wchar_t filenameW[MAX_PATH]; if (!GetTempFileNameW(directoryW.c_str(), L"msx", 0, filenameW)) { throw FileException(StringOp::Builder() << "GetTempFileNameW failed: " << GetLastError()); } filename = utf16to8(filenameW); FILE* fp = _wfopen(filenameW, L"wb"); #else filename = directory + "/XXXXXX"; int fd = mkstemp(const_cast(filename.c_str())); if (fd == -1) { throw FileException("Coundn't get temp file name"); } FILE* fp = fdopen(fd, "wb"); #endif return fp; } } // namespace FileOperations } // namespace openmsx openmsx-0.10.0/src/file/ZipFileAdapter.cc0000644000175000017500000000223412262345041020725 0ustar manuelmanuel00000000000000#include "ZipFileAdapter.hh" #include "ZlibInflate.hh" #include "FileException.hh" namespace openmsx { ZipFileAdapter::ZipFileAdapter(std::unique_ptr file_) : CompressedFileAdapter(std::move(file_)) { } void ZipFileAdapter::decompress(FileBase& file, Decompressed& decompressed) { size_t size; const byte* data = file.mmap(size); ZlibInflate zlib(data, size); if (zlib.get32LE() != 0x04034B50) { throw FileException("Invalid ZIP file"); } // skip "version needed to extract" and "general purpose bit flag" zlib.skip(2 + 2); // compression method if (zlib.get16LE() != 0x0008) { throw FileException("Unsupported zip compression method"); } // skip "last mod file time", "last mod file data", // "crc32", "compressed size" zlib.skip(2 + 2 + 4 + 4); unsigned origSize = zlib.get32LE(); // uncompressed size unsigned filenameLen = zlib.get16LE(); // filename length unsigned extraFieldLen = zlib.get16LE(); // extra field length decompressed.originalName = zlib.getString(filenameLen); // original filename zlib.skip(extraFieldLen); // skip "extra field" zlib.inflate(decompressed.buf, origSize); } } // namespace openmsx openmsx-0.10.0/src/file/FilePool.hh0000644000175000017500000000524712262345041017614 0ustar manuelmanuel00000000000000#ifndef FILEPOOL_HH #define FILEPOOL_HH #include "FileOperations.hh" #include "Observer.hh" #include "EventListener.hh" #include "sha1.hh" #include "noncopyable.hh" #include #include #include #include #include #include namespace openmsx { class CommandController; class EventDistributor; class File; class Setting; class StringSetting; class CliComm; class FilePool : private Observer, private EventListener, private noncopyable { public: FilePool(CommandController& controler, EventDistributor& distributor); ~FilePool(); enum FileType { SYSTEM_ROM = 1, ROM = 2, DISK = 4, TAPE = 8 }; /** Search file with the given sha1sum. * If found it returns the (already opened) file, * if not found it returns nullptr. */ std::unique_ptr getFile(FileType fileType, const Sha1Sum& sha1sum); /** Calculate sha1sum for the given File object. * If possible the result is retrieved from cache, avoiding the * relatively expensive calculation. */ Sha1Sum getSha1Sum(File& file); /** Remove sha1sum for this file from the cache. * When the file was written to, sha1sum changes and it should be * removed from the cache. */ void removeSha1Sum(File& file); private: struct Entry { std::string path; int types; }; typedef std::vector Directories; // , sorted on sha1sum typedef std::vector> Pool; void insert(const Sha1Sum& sum, time_t time, const std::string& filename); void remove(Pool::iterator it); bool adjust(Pool::iterator it, const Sha1Sum& newSum); void readSha1sums(); void writeSha1sums(); std::unique_ptr getFromPool(const Sha1Sum& sha1sum); std::unique_ptr scanDirectory(const Sha1Sum& sha1sum, const std::string& directory, const std::string& poolPath); std::unique_ptr scanFile(const Sha1Sum& sha1sum, const std::string& filename, const FileOperations::Stat& st, const std::string& poolPath); Pool::iterator findInDatabase(const std::string& filename); Directories getDirectories() const; // Observer void update(const Setting& setting); // EventListener virtual int signalEvent(const std::shared_ptr& event); const std::unique_ptr filePoolSetting; EventDistributor& distributor; CliComm& cliComm; Pool pool; uint64_t lastTime; // to indicate progress unsigned amountScanned; // to indicate progress bool quit; bool needWrite; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/GZFileAdapter.hh0000644000175000017500000000052612262345041020517 0ustar manuelmanuel00000000000000#ifndef GZFILEADAPTER_HH #define GZFILEADAPTER_HH #include "CompressedFileAdapter.hh" namespace openmsx { class GZFileAdapter : public CompressedFileAdapter { public: explicit GZFileAdapter(std::unique_ptr file); private: virtual void decompress(FileBase& file, Decompressed& decompressed); }; } // namespace openmsx #endif openmsx-0.10.0/src/file/FileNotFoundException.hh0000644000175000017500000000044712262345041022313 0ustar manuelmanuel00000000000000#ifndef FILENOTFOUNDEXCEPTION_HH #define FILENOTFOUNDEXCEPTION_HH #include "FileException.hh" namespace openmsx { class FileNotFoundException : public FileException { public: explicit FileNotFoundException(string_ref message) : FileException(message) {} }; } // namespace openmsx #endif openmsx-0.10.0/src/file/LocalFileReference.cc0000644000175000017500000000370012262345041021532 0ustar manuelmanuel00000000000000#include "LocalFileReference.hh" #include "File.hh" #include "FileOperations.hh" #include "FileException.hh" #include "StringOp.hh" #include "build-info.hh" #include #include #include using std::string; namespace openmsx { LocalFileReference::LocalFileReference(const Filename& filename) { init(filename.getResolved()); } LocalFileReference::LocalFileReference(const string& url) { init(url); } LocalFileReference::LocalFileReference(File& file) { init(file); } void LocalFileReference::init(const string& url) { File file(url); init(file); } void LocalFileReference::init(File& file) { tmpFile = file.getLocalReference(); if (!tmpFile.empty()) { // file is backed on the (local) filesystem, // we can simply use the path to that file assert(tmpDir.empty()); // no need to delete file/dir later return; } // create temp dir #if defined(_WIN32) || PLATFORM_ANDROID tmpDir = FileOperations::getTempDir() + FileOperations::nativePathSeparator + "openmsx"; #else // TODO - why not just use getTempDir()? tmpDir = StringOp::Builder() << "/tmp/openmsx." << int(getpid()); #endif // it's possible this directory already exists, in that case the // following function does nothing FileOperations::mkdirp(tmpDir); // create temp file FILE* fp = FileOperations::openUniqueFile(tmpDir, tmpFile); if (!fp) { throw FileException("Couldn't create temp file"); } // write temp file size_t size; const byte* buf = file.mmap(size); if (fwrite(buf, 1, size, fp) != size) { throw FileException("Couldn't write temp file"); } fclose(fp); } LocalFileReference::~LocalFileReference() { if (!tmpDir.empty()) { FileOperations::unlink(tmpFile); // it's possible the directory is not empty, in that case // the following function will fail, we ignore that error FileOperations::rmdir(tmpDir); } } const string LocalFileReference::getFilename() const { assert(!tmpFile.empty()); return tmpFile; } } // namespace openmsx openmsx-0.10.0/src/file/Filename.hh0000644000175000017500000000314412262345041017615 0ustar manuelmanuel00000000000000#ifndef FILENAME_HH #define FILENAME_HH #include namespace openmsx { class FileContext; class CommandController; /** This class represents a filename. * A filename is resolved in a certain context. * It is possible to query both the resolved and unresolved filename. * * Most file operations will want the resolved name, but for example * for savestates we (also) want the unresolved name. */ class Filename { public: Filename(); explicit Filename(const std::string& filename); Filename(const std::string& filename, const FileContext& context); const std::string& getOriginal() const; const std::string& getResolved() const; /** After a loadstate we prefer to use the exact same file as before * savestate. But if that file is not available (possibly because * snapshot is loaded on a different host machine), we fallback to * the original filename. */ void updateAfterLoadState(); /** Convenience method to test for empty filename. * In any case getOriginal().empty() and getResolved().empty() return * the same result. This method is a shortcut to either of these. */ bool empty() const; /** Change the resolved part of this filename * E.g. on loadstate when we didn't find the original file, but another * file with a matching checksum. */ void setResolved(const std::string& resolved); template void serialize(Archive& ar, unsigned version); private: // non-const because we want this class to be assignable // (to be able to store them in std::vector) std::string originalFilename; std::string resolvedFilename; }; } // namespace openmsx #endif openmsx-0.10.0/src/file/File.cc0000644000175000017500000000530412262345041016742 0ustar manuelmanuel00000000000000#include "File.hh" #include "Filename.hh" #include "FilePool.hh" #include "LocalFile.hh" #include "GZFileAdapter.hh" #include "ZipFileAdapter.hh" #include "StringOp.hh" #include "checked_cast.hh" #include "memory.hh" #include #include using std::string; namespace openmsx { static std::unique_ptr init(string_ref url, File::OpenMode mode) { static const byte GZ_HEADER[3] = { 0x1F, 0x8B, 0x08 }; static const byte ZIP_HEADER[4] = { 0x50, 0x4B, 0x03, 0x04 }; std::unique_ptr file = make_unique(url, mode); if (file->getSize() >= 4) { byte buf[4]; file->read(buf, 4); file->seek(0); if (memcmp(buf, GZ_HEADER, 3) == 0) { file = make_unique(std::move(file)); } else if (memcmp(buf, ZIP_HEADER, 4) == 0) { file = make_unique(std::move(file)); } else { // only pre-cache non-compressed files if (mode == File::PRE_CACHE) { checked_cast(file.get())->preCacheFile(); } } } return file; } File::File(const Filename& filename, OpenMode mode) : file(init(filename.getResolved(), mode)) , filepool(nullptr) { } File::File(string_ref url, OpenMode mode) : file(init(url, mode)) , filepool(nullptr) { } File::File(string_ref filename, const char* mode) : file(make_unique(filename, mode)) , filepool(nullptr) { } File::File(const Filename& filename, const char* mode) : file(make_unique(filename.getResolved(), mode)) , filepool(nullptr) { } File::~File() { } void File::read(void* buffer, size_t num) { file->read(buffer, num); } void File::write(const void* buffer, size_t num) { if (filepool) { filepool->removeSha1Sum(*this); } file->write(buffer, num); } const byte* File::mmap(size_t& size) { return file->mmap(size); } void File::munmap() { file->munmap(); } size_t File::getSize() { return file->getSize(); } void File::seek(size_t pos) { file->seek(pos); } size_t File::getPos() { return file->getPos(); } void File::truncate(size_t size) { return file->truncate(size); } void File::flush() { file->flush(); } const string File::getURL() const { return file->getURL(); } const string File::getLocalReference() const { return file->getLocalReference(); } const string File::getOriginalName() { string orig = file->getOriginalName(); return !orig.empty() ? orig : getURL(); } bool File::isReadOnly() const { return file->isReadOnly(); } time_t File::getModificationDate() { return file->getModificationDate(); } Sha1Sum File::getSha1Sum() { assert(filepool); // must be set return filepool->getSha1Sum(*this); } void File::setFilePool(FilePool& filepool_) { assert(!filepool); // can only be set once filepool = &filepool_; } } // namespace openmsx openmsx-0.10.0/src/EmptyPatch.cc0000644000175000017500000000162412262345041017223 0ustar manuelmanuel00000000000000#include "EmptyPatch.hh" #include #include namespace openmsx { EmptyPatch::EmptyPatch(const byte* block_, size_t size_) : block(block_), size(size_) { } void EmptyPatch::copyBlock(size_t src, byte* dst, size_t num) const { if ((src + num) > size) { // past end if (size <= src) { // start past size, only fill block memset(dst, 0, num); } else { auto part1 = size - src; auto part2 = num - part1; assert(dst != (block + src)); memcpy(dst, &block[src], part1); memset(dst + part1, 0, part2); } } else { if (dst != (block + src)) { // memcpy cannot handle overlapping regions, but in // that case we don't need to copy at all memcpy(dst, &block[src], num); } } } size_t EmptyPatch::getSize() const { return size; } std::vector EmptyPatch::getFilenames() const { // return {}; return std::vector(); } } // namespace openmsx openmsx-0.10.0/src/sound/0000755000175000017500000000000012262345041015763 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/sound/Y8950KeyboardDevice.cc0000644000175000017500000000024412262345041021631 0ustar manuelmanuel00000000000000#include "Y8950KeyboardDevice.hh" namespace openmsx { string_ref Y8950KeyboardDevice::getClass() const { return "Y8950 Keyboard Port"; } } // namespace openmsx openmsx-0.10.0/src/sound/Y8950.hh0000644000175000017500000000344312262345041017046 0ustar manuelmanuel00000000000000#ifndef Y8950_HH #define Y8950_HH #include "EmuTime.hh" #include "openmsx.hh" #include #include namespace openmsx { class MSXAudio; class MSXMotherBoard; class DeviceConfig; class Y8950 { public: static const int CLOCK_FREQ = 3579545; static const int CLOCK_FREQ_DIV = 72; // Bitmask for register 0x04 // Timer1 Start. static const int R04_ST1 = 0x01; // Timer2 Start. static const int R04_ST2 = 0x02; // not used //static const int R04 = 0x04; // Mask 'Buffer Ready'. static const int R04_MASK_BUF_RDY = 0x08; // Mask 'End of sequence'. static const int R04_MASK_EOS = 0x10; // Mask Timer2 flag. static const int R04_MASK_T2 = 0x20; // Mask Timer1 flag. static const int R04_MASK_T1 = 0x40; // IRQ RESET. static const int R04_IRQ_RESET = 0x80; // Bitmask for status register static const int STATUS_EOS = R04_MASK_EOS; static const int STATUS_BUF_RDY = R04_MASK_BUF_RDY; static const int STATUS_T2 = R04_MASK_T2; static const int STATUS_T1 = R04_MASK_T1; Y8950(const std::string& name, const DeviceConfig& config, unsigned sampleRam, EmuTime::param time, MSXAudio& audio); ~Y8950(); void setEnabled(bool enabled, EmuTime::param time); void clearRam(); void reset(EmuTime::param time); void writeReg(byte reg, byte data, EmuTime::param time); byte readReg(byte reg, EmuTime::param time); byte peekReg(byte reg, EmuTime::param time) const; byte readStatus(EmuTime::param time); byte peekStatus(EmuTime::param time) const; // for ADPCM void setStatus(byte flags); void resetStatus(byte flags); byte peekRawStatus() const; template void serialize(Archive& ar, unsigned version); private: class Impl; const std::unique_ptr pimpl; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/DirectXSoundDriver.cc0000644000175000017500000001563412262345041022032 0ustar manuelmanuel00000000000000#ifdef _WIN32 #include "DirectXSoundDriver.hh" #include "MSXException.hh" #include "openmsx.hh" #include "sdlwin32.hh" #include #include namespace openmsx { static const int BYTES_PER_SAMPLE = 2; static const int CHANNELS = 2; static HWND getWindowHandle() { // This is SDL specific code, refactor when needed // !! Initialize video system, DirectX needs a handle to the window // !! and this only works when SDL video part is initialized if (!SDL_WasInit(SDL_INIT_VIDEO)) SDL_InitSubSystem(SDL_INIT_VIDEO); return getSDLWindowHandle(); } DirectXSoundDriver::DirectXSoundDriver(unsigned sampleRate, unsigned samples) { if (DirectSoundCreate(nullptr, &directSound, nullptr) != DS_OK) { throw MSXException("Couldn't initialize DirectSound driver"); } HWND hwnd = getWindowHandle(); if (IDirectSound_SetCooperativeLevel( directSound, hwnd, DSSCL_EXCLUSIVE) != DS_OK) { throw MSXException("Couldn't initialize DirectSound driver"); } DSCAPS capabilities; memset(&capabilities, 0, sizeof(capabilities)); capabilities.dwSize = sizeof(capabilities); IDirectSound_GetCaps(directSound, &capabilities); if (!((capabilities.dwFlags & DSCAPS_PRIMARY16BIT) || (capabilities.dwFlags & DSCAPS_SECONDARY16BIT))) { // no 16 bits per sample throw MSXException("Couldn't configure 16 bit per sample"); } if (!((capabilities.dwFlags & DSCAPS_PRIMARYSTEREO) || (capabilities.dwFlags & DSCAPS_SECONDARYSTEREO))) { // no stereo throw MSXException("Couldn't configure stereo mode"); } PCMWAVEFORMAT pcmwf; memset(&pcmwf, 0, sizeof(pcmwf)); pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; pcmwf.wf.nChannels = CHANNELS; pcmwf.wf.nSamplesPerSec = sampleRate; pcmwf.wBitsPerSample = 8 * BYTES_PER_SAMPLE; pcmwf.wf.nBlockAlign = CHANNELS * BYTES_PER_SAMPLE; pcmwf.wf.nAvgBytesPerSec = pcmwf.wf.nSamplesPerSec * pcmwf.wf.nBlockAlign; DSBUFFERDESC desc; memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); desc.dwFlags = DSBCAPS_PRIMARYBUFFER; bufferSize = 2 * samples * BYTES_PER_SAMPLE * CHANNELS; fragmentSize = 1; while (bufferSize / fragmentSize >= 32 || fragmentSize < 512) { fragmentSize <<= 1; } DWORD fragmentCount = 1 + bufferSize / fragmentSize; while (fragmentCount < 8) { fragmentCount *= 2; fragmentSize /= 2; } bufferSize = fragmentCount * fragmentSize; if (IDirectSound_CreateSoundBuffer( directSound, &desc, &secondaryBuffer, nullptr) != DS_OK) { throw MSXException("Couldn't initialize DirectSound driver"); } memset(&desc, 0, sizeof(desc)); desc.dwSize = sizeof(desc); desc.dwFlags = DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS; desc.dwBufferBytes = bufferSize; desc.lpwfxFormat = reinterpret_cast(&pcmwf); if (IDirectSound_CreateSoundBuffer( directSound, &desc, &primaryBuffer, nullptr) != DS_OK) { throw MSXException("Couldn't initialize DirectSound driver"); } WAVEFORMATEX wfex; memset(&wfex, 0, sizeof(wfex)); wfex.wFormatTag = WAVE_FORMAT_PCM; wfex.nChannels = CHANNELS; wfex.nSamplesPerSec = sampleRate; wfex.wBitsPerSample = 8 * BYTES_PER_SAMPLE; wfex.nBlockAlign = CHANNELS * BYTES_PER_SAMPLE; wfex.nAvgBytesPerSec = wfex.nSamplesPerSec * wfex.nBlockAlign; if (IDirectSoundBuffer_SetFormat(secondaryBuffer, &wfex) != DS_OK) { throw MSXException("Couldn't initialize DirectSound driver"); } bufferOffset = bufferSize; dxClear(); skipCount = 0; state = DX_SOUND_DISABLED; frequency = sampleRate; } DirectXSoundDriver::~DirectXSoundDriver() { IDirectSoundBuffer_Stop(primaryBuffer); IDirectSoundBuffer_Release(primaryBuffer); IDirectSound_Release(directSound); } void DirectXSoundDriver::mute() { dxClear(); IDirectSoundBuffer_Stop(primaryBuffer); state = DX_SOUND_DISABLED; } void DirectXSoundDriver::unmute() { state = DX_SOUND_ENABLED; } unsigned DirectXSoundDriver::getFrequency() const { return frequency; } unsigned DirectXSoundDriver::getSamples() const { return fragmentSize; } // #define MAKE_HRESULT(s,f,c) ((HRESULT)(((unsigned long)(s)<<31)|((unsigned long)(f)<<16)|((unsigned long)(c)))) // #define MAKE_DSHRESULT(code) MAKE_HRESULT(1, _FACDS, code) // #define DSERR_BUFFERLOST MAKE_DSHRESULT(150) static const HRESULT OPENMSX_DSERR_BUFFERLOST = (1u << 31) | (_FACDS << 16) | 150; void DirectXSoundDriver::dxClear() { void *audioBuffer1, *audioBuffer2; DWORD audioSize1, audioSize2; if (IDirectSoundBuffer_Lock( primaryBuffer, 0, bufferSize, &audioBuffer1, &audioSize1, &audioBuffer2, &audioSize2, 0) == OPENMSX_DSERR_BUFFERLOST) { IDirectSoundBuffer_Restore(primaryBuffer); } else { memset(audioBuffer1, 0, audioSize1); if (audioBuffer2) { memset(audioBuffer2, 0, audioSize2); } IDirectSoundBuffer_Unlock( primaryBuffer, audioBuffer1, audioSize1, audioBuffer2, audioSize2); } } int DirectXSoundDriver::dxCanWrite(unsigned start, unsigned size) { DWORD readPos, writePos; IDirectSoundBuffer_GetCurrentPosition( primaryBuffer, &readPos, &writePos); unsigned end = start + size; if (writePos < readPos) writePos += bufferSize; if (start < readPos) start += bufferSize; if (end < readPos) end += bufferSize; if ((start < writePos) || (end < writePos)) { return (bufferSize - (writePos - readPos)) / 2 - fragmentSize; } else { return 0; } } void DirectXSoundDriver::dxWriteOne(short* buffer, unsigned lockSize) { void *audioBuffer1, *audioBuffer2; DWORD audioSize1, audioSize2; do { if (IDirectSoundBuffer_Lock( primaryBuffer, bufferOffset, lockSize, &audioBuffer1, &audioSize1, &audioBuffer2, &audioSize2, 0) == OPENMSX_DSERR_BUFFERLOST) { IDirectSoundBuffer_Restore(primaryBuffer); IDirectSoundBuffer_Lock( primaryBuffer, bufferOffset, lockSize, &audioBuffer1, &audioSize1, &audioBuffer2, &audioSize2, 0); } } while ((audioSize1 + audioSize2) < lockSize); memcpy(audioBuffer1, buffer, audioSize1); if (audioBuffer2) { memcpy(audioBuffer2, reinterpret_cast(buffer) + audioSize1, audioSize2); } IDirectSoundBuffer_Unlock(primaryBuffer, audioBuffer1, audioSize1, audioBuffer2, audioSize2); bufferOffset += lockSize; bufferOffset %= bufferSize; } void DirectXSoundDriver::uploadBuffer(short* buffer, unsigned count) { if (state == DX_SOUND_DISABLED) return; if (state == DX_SOUND_ENABLED) { DWORD readPos, writePos; IDirectSoundBuffer_GetCurrentPosition( primaryBuffer, &readPos, &writePos); bufferOffset = (readPos + bufferSize / 2) % bufferSize; if (IDirectSoundBuffer_Play(primaryBuffer, 0, 0, DSBPLAY_LOOPING) == OPENMSX_DSERR_BUFFERLOST) { IDirectSoundBuffer_Play( primaryBuffer, 0, 0, DSBPLAY_LOOPING); } state = DX_SOUND_RUNNING; } count *= (CHANNELS * BYTES_PER_SAMPLE); if (skipCount > 0) { skipCount -= count; return; } skipCount = dxCanWrite(bufferOffset, count); if (skipCount <= 0) { dxWriteOne(buffer, count); } } } // namespace openmsx #endif // _WIN32 openmsx-0.10.0/src/sound/Y8950Adpcm.cc0000644000175000017500000003466012262345041020006 0ustar manuelmanuel00000000000000// The actual sample playing part is duplicated for the 'emu' domain and the // 'audio' domain. The emu part is responsible for cycle accurate sample // readback (see peekReg() register 0x13 and 0x14) and for cycle accurate // status register updates (the status bits related to playback, e.g. // end-of-sample). The audio part is responsible for the actual sound // generation. This split up allows for the two parts to be out-of-sync. So for // example when emulation is running faster or slower than 100% realtime speed, // we both get cycle accurate emulation behaviour and still sound generation at // 100% realtime speed (which is most of the time better for sound quality). #include "Y8950Adpcm.hh" #include "Clock.hh" #include "Ram.hh" #include "DeviceConfig.hh" #include "MSXMotherBoard.hh" #include "Math.hh" #include "serialize.hh" #include "memory.hh" #include namespace openmsx { // Bitmask for register 0x07 static const int R07_RESET = 0x01; static const int R07_SP_OFF = 0x08; static const int R07_REPEAT = 0x10; static const int R07_MEMORY_DATA = 0x20; static const int R07_REC = 0x40; static const int R07_START = 0x80; static const int R07_MODE = 0xE0; // Bitmask for register 0x08 static const int R08_ROM = 0x01; static const int R08_64K = 0x02; static const int R08_DA_AD = 0x04; static const int R08_SAMPL = 0x08; static const int R08_NOTE_SET = 0x40; static const int R08_CSM = 0x80; static const int DMAX = 0x6000; static const int DMIN = 0x7F; static const int DDEF = 0x7F; static const int STEP_BITS = 16; static const int STEP_MASK = (1 << STEP_BITS) -1; Y8950Adpcm::Y8950Adpcm(Y8950& y8950_, const DeviceConfig& config, const std::string& name, unsigned sampleRam) : Schedulable(config.getScheduler()) , y8950(y8950_) , ram(make_unique( config, name + " RAM", "Y8950 sample RAM", sampleRam)) , clock(config.getMotherBoard().getCurrentTime()) , volume(0) { clearRam(); } Y8950Adpcm::~Y8950Adpcm() { } void Y8950Adpcm::clearRam() { ram->clear(0xFF); } void Y8950Adpcm::reset(EmuTime::param time) { removeSyncPoint(); clock.reset(time); startAddr = 0; stopAddr = 7; delta = 0; addrMask = (1 << 18) - 1; reg7 = 0; reg15 = 0; readDelay = 0; romBank = false; writeReg(0x12, 255, time); // volume restart(emu); restart(aud); y8950.setStatus(Y8950::STATUS_BUF_RDY); } bool Y8950Adpcm::isPlaying() const { return (reg7 & 0xC0) == 0x80; } bool Y8950Adpcm::isMuted() const { return !isPlaying() || (reg7 & R07_SP_OFF); } void Y8950Adpcm::restart(PlayData& pd) { pd.memPntr = startAddr; pd.nowStep = (1 << STEP_BITS) - delta; pd.out = 0; pd.output = 0; pd.diff = DDEF; pd.nextLeveling = 0; pd.sampleStep = 0; pd.adpcm_data = 0; // dummy, avoid UMR in serialize } void Y8950Adpcm::sync(EmuTime::param time) { if (isPlaying()) { // optimization, also correct without this test unsigned ticks = clock.getTicksTill(time); for (unsigned i = 0; isPlaying() && (i < ticks); ++i) { calcSample(true); // ignore result } } clock.advance(time); } void Y8950Adpcm::schedule() { assert(isPlaying()); if ((stopAddr > startAddr) && (delta != 0)) { // TODO possible optimization, no need to set sync points if // the corresponding bit is masked in the interupt enable // register if (reg7 & R07_MEMORY_DATA) { // we already did a sync(time), so clock is up-to-date Clock stop(clock); uint64_t samples = stopAddr - emu.memPntr + 1; uint64_t length = (samples << STEP_BITS) + ((1 << STEP_BITS) - emu.nowStep) + (delta - 1); stop += unsigned(length / delta); setSyncPoint(stop.getTime()); } else { // TODO we should also set a syncpoint in this case // because this mode sets the STATUS_BUF_RDY bit // which also triggers an IRQ } } } void Y8950Adpcm::executeUntil(EmuTime::param time, int /*userData*/) { assert(isPlaying()); sync(time); // should set STATUS_EOS assert(y8950.peekRawStatus() & Y8950::STATUS_EOS); if (isPlaying() && (reg7 & R07_REPEAT)) { schedule(); } } void Y8950Adpcm::writeReg(byte rg, byte data, EmuTime::param time) { sync(time); // TODO only when needed switch (rg) { case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET reg7 = data; if (reg7 & R07_RESET) { reg7 = 0; } if (reg7 & R07_START) { // start ADPCM restart(emu); restart(aud); } if (reg7 & R07_MEMORY_DATA) { // access external memory? emu.memPntr = startAddr; aud.memPntr = startAddr; readDelay = 2; // two dummy reads if ((reg7 & 0xA0) == 0x20) { // Memory read or write y8950.setStatus(Y8950::STATUS_BUF_RDY); } } else { // access via CPU emu.memPntr = 0; aud.memPntr = 0; } removeSyncPoint(); if (isPlaying()) { schedule(); } break; case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM romBank = data & R08_ROM; addrMask = data & R08_64K ? (1 << 16) - 1 : (1 << 18) - 1; break; case 0x09: // START ADDRESS (L) startAddr = (startAddr & 0x7F807) | (data << 3); break; case 0x0A: // START ADDRESS (H) startAddr = (startAddr & 0x007FF) | (data << 11); break; case 0x0B: // STOP ADDRESS (L) stopAddr = (stopAddr & 0x7F807) | (data << 3); if (isPlaying()) { removeSyncPoint(); schedule(); } break; case 0x0C: // STOP ADDRESS (H) stopAddr = (stopAddr & 0x007FF) | (data << 11); if (isPlaying()) { removeSyncPoint(); schedule(); } break; case 0x0F: // ADPCM-DATA writeData(data); break; case 0x10: // DELTA-N (L) delta = (delta & 0xFF00) | data; volumeWStep = (volume * delta) >> STEP_BITS; if (isPlaying()) { removeSyncPoint(); schedule(); } break; case 0x11: // DELTA-N (H) delta = (delta & 0x00FF) | (data << 8); volumeWStep = (volume * delta) >> STEP_BITS; if (isPlaying()) { removeSyncPoint(); schedule(); } break; case 0x12: { // ENVELOP CONTROL volume = data; volumeWStep = (volume * delta) >> STEP_BITS; break; } case 0x0D: // PRESCALE (L) case 0x0E: // PRESCALE (H) case 0x15: // DAC-DATA (bit9-2) case 0x16: // (bit1-0) case 0x17: // (exponent) case 0x1A: // PCM-DATA // not implemented break; } } void Y8950Adpcm::writeData(byte data) { reg15 = data; if ((reg7 & R07_MODE) == 0x60) { // external memory write assert(!isPlaying()); // no need to update the 'aud' data if (readDelay) { emu.memPntr = startAddr; readDelay = 0; } if (emu.memPntr <= stopAddr) { writeMemory(emu.memPntr, data); emu.memPntr += 2; // two nibbles at a time // reset BRDY bit in status register, // which means we are processing the write y8950.resetStatus(Y8950::STATUS_BUF_RDY); // setup a timer that will callback us in 10 // master clock cycles for Y8950. In the // callback set the BRDY flag to 1 , which // means we have written the data. For now, we // don't really do this; we simply reset and // set the flag in zero time, so that the IRQ // will work. // set BRDY bit in status register y8950.setStatus(Y8950::STATUS_BUF_RDY); } else { // set EOS bit in status register y8950.setStatus(Y8950::STATUS_EOS); } } else if ((reg7 & R07_MODE) == 0x80) { // ADPCM synthesis from CPU // Reset BRDY bit in status register, which means we // are full of data y8950.resetStatus(Y8950::STATUS_BUF_RDY); } } byte Y8950Adpcm::readReg(byte rg, EmuTime::param time) { sync(time); // TODO only when needed byte result = (rg == 0x0F) ? readData() // ADPCM-DATA : peekReg(rg); // other return result; } byte Y8950Adpcm::peekReg(byte rg, EmuTime::param time) { sync(time); // TODO only when needed return peekReg(rg); } byte Y8950Adpcm::peekReg(byte rg) const { switch (rg) { case 0x0F: // ADPCM-DATA return peekData(); case 0x13: // TODO check: is this before or after // volume is applied // filtering is performed return (emu.output >> 8) & 0xFF; case 0x14: return emu.output >> 16; default: return 255; } } void Y8950Adpcm::resetStatus() { // If the BUF_RDY mask is cleared (e.g. by writing the value 0x80 to // register R#4). Reading the status register still has the BUF_RDY // bit set. Without this behavior demos like 'NOP Unknown reality' // hang when testing the amount of sample ram or when uploading data // to the sample ram. // // Before this code was added, those demos also worked but only // because we had a hack that always kept bit BUF_RDY set. // // When the ADPCM unit is not performing any function (e.g. after a // reset), the BUF_RDY bit should still be set. The AUDIO detection // routine in 'MSX-Audio BIOS v1.3' depends on this. See // [3533002] Y8950 not being detected by MSX-Audio v1.3 // https://sourceforge.net/tracker/?func=detail&aid=3533002&group_id=38274&atid=421861 // TODO I've implemented this as '(reg7 & R07_MODE) == 0', is this // correct/complete? if (((reg7 & R07_MODE & ~R07_REC) == R07_MEMORY_DATA) || ((reg7 & R07_MODE) == 0)){ // transfer to or from sample ram, or no function y8950.setStatus(Y8950::STATUS_BUF_RDY); } } byte Y8950Adpcm::readData() { if ((reg7 & R07_MODE) == R07_MEMORY_DATA) { // external memory read assert(!isPlaying()); // no need to update the 'aud' data if (readDelay) { emu.memPntr = startAddr; } } byte result = peekData(); if ((reg7 & R07_MODE) == R07_MEMORY_DATA) { assert(!isPlaying()); // no need to update the 'aud' data if (readDelay) { // two dummy reads --readDelay; y8950.setStatus(Y8950::STATUS_BUF_RDY); } else if (emu.memPntr > stopAddr) { // set EOS bit in status register y8950.setStatus(Y8950::STATUS_EOS); } else { emu.memPntr += 2; // two nibbles at a time // reset BRDY bit in status register, which means we // are reading the memory now y8950.resetStatus(Y8950::STATUS_BUF_RDY); // setup a timer that will callback us in 10 master // clock cycles for Y8950. In the callback set the BRDY // flag to 1, which means we have another data ready. // For now, we don't really do this; we simply reset and // set the flag in zero time, so that the IRQ will work. // set BRDY bit in status register y8950.setStatus(Y8950::STATUS_BUF_RDY); } } return result; } byte Y8950Adpcm::peekData() const { if ((reg7 & R07_MODE) == R07_MEMORY_DATA) { // external memory read assert(!isPlaying()); // no need to update the 'aud' data if (readDelay) { return reg15; } else if (emu.memPntr > stopAddr) { return 0; } else { return readMemory(emu.memPntr); } } else { return 0; // TODO check } } void Y8950Adpcm::writeMemory(unsigned memPntr, byte value) { unsigned addr = (memPntr / 2) & addrMask; if ((addr < ram->getSize()) && !romBank) { (*ram)[addr] = value; } } byte Y8950Adpcm::readMemory(unsigned memPntr) const { unsigned addr = (memPntr / 2) & addrMask; if (romBank || (addr >= ram->getSize())) { return 0; // checked on a real machine } else { return (*ram)[addr]; } } int Y8950Adpcm::calcSample() { // called by audio thread if (!isPlaying()) return 0; int output = calcSample(false); return (reg7 & R07_SP_OFF) ? 0 : output; } int Y8950Adpcm::calcSample(bool doEmu) { // values taken from ymdelta.c by Tatsuyuki Satoh. static const int F1[16] = { 1, 3, 5, 7, 9, 11, 13, 15, -1, -3, -5, -7, -9, -11, -13, -15 }; static const int F2[16] = { 57, 57, 57, 57, 77, 102, 128, 153, 57, 57, 57, 57, 77, 102, 128, 153 }; assert(isPlaying()); PlayData& pd = doEmu ? emu : aud; pd.nowStep += delta; if (pd.nowStep & ~STEP_MASK) { pd.nowStep &= STEP_MASK; byte val; if (!(pd.memPntr & 1)) { // even nibble if (reg7 & R07_MEMORY_DATA) { pd.adpcm_data = readMemory(pd.memPntr); } else { pd.adpcm_data = reg15; // set BRDY bit, ready to accept new data if (doEmu) { y8950.setStatus(Y8950::STATUS_BUF_RDY); } } val = pd.adpcm_data >> 4; } else { // odd nibble val = pd.adpcm_data & 0x0F; } int prevOut = pd.out; pd.out = Math::clipIntToShort(pd.out + (pd.diff * F1[val]) / 8); pd.diff = Math::clip((pd.diff * F2[val]) / 64); int prevLeveling = pd.nextLeveling; pd.nextLeveling = (prevOut + pd.out) / 2; int deltaLeveling = pd.nextLeveling - prevLeveling; pd.sampleStep = deltaLeveling * volumeWStep; int tmp = deltaLeveling * ((volume * pd.nowStep) >> STEP_BITS); pd.output = prevLeveling * volume + tmp; ++pd.memPntr; if ((reg7 & R07_MEMORY_DATA) && (pd.memPntr > stopAddr)) { // On 2003/06/21 I commited a patch with comment: // generate end-of-sample interrupt at every sample // end, including loops // Unfortunatly it doesn't give any reason why and now // I can't remember it :-( // This is different from e.g. the MAME implementation. if (doEmu) { y8950.setStatus(Y8950::STATUS_EOS); } if (reg7 & R07_REPEAT) { restart(pd); } else { if (doEmu) { removeSyncPoint(); reg7 = 0; } } } } else { pd.output += pd.sampleStep; } return pd.output >> 12; } // version 1: // Initial verson // version 2: // - Split PlayData in emu and audio part (though this doesn't add new state // to the savestate). // - Added clock object. template void Y8950Adpcm::serialize(Archive& ar, unsigned version) { ar.template serializeBase(*this); ar.serialize("ram", *ram); ar.serialize("startAddr", startAddr); ar.serialize("stopAddr", stopAddr); ar.serialize("addrMask", addrMask); ar.serialize("volume", volume); ar.serialize("volumeWStep", volumeWStep); ar.serialize("readDelay", readDelay); ar.serialize("delta", delta); ar.serialize("reg7", reg7); ar.serialize("reg15", reg15); ar.serialize("romBank", romBank); ar.serialize("memPntr", emu.memPntr); ar.serialize("nowStep", emu.nowStep); ar.serialize("out", emu.out); ar.serialize("output", emu.output); ar.serialize("diff", emu.diff); ar.serialize("nextLeveling", emu.nextLeveling); ar.serialize("sampleStep", emu.sampleStep); ar.serialize("adpcm_data", emu.adpcm_data); if (ar.isLoader()) { // ignore aud part for saving, // for loading we make it the same as the emu part aud = emu; } if (ar.versionBelow(version, 2)) { clock.reset(getCurrentTime()); // reschedule, because automatically deserialized sync-point // can be off, because clock.getTime() != getCurrentTime() removeSyncPoint(); if (isPlaying()) { schedule(); } } else { ar.serialize("clock", clock); } } INSTANTIATE_SERIALIZE_METHODS(Y8950Adpcm); } // namespace openmsx openmsx-0.10.0/src/sound/WavAudioInput.hh0000644000175000017500000000176512262345041021054 0ustar manuelmanuel00000000000000#ifndef WAVAUDIOINPUT_HH #define WAVAUDIOINPUT_HH #include "AudioInputDevice.hh" #include "WavData.hh" #include "Observer.hh" #include "EmuTime.hh" #include "serialize_meta.hh" #include namespace openmsx { class CommandController; class FilenameSetting; class Setting; class WavAudioInput : public AudioInputDevice, private Observer { public: explicit WavAudioInput(CommandController& commandController); virtual ~WavAudioInput(); // AudioInputDevice virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); virtual short readSample(EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: void loadWave(); void update(const Setting& setting); const std::unique_ptr audioInputFilenameSetting; WavData wav; EmuTime reference; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/KeyClick.hh0000644000175000017500000000071512262345041020005 0ustar manuelmanuel00000000000000#ifndef KEYCLICK_HH #define KEYCLICK_HH #include "EmuTime.hh" #include "noncopyable.hh" #include namespace openmsx { class DeviceConfig; class DACSound8U; class KeyClick : private noncopyable { public: explicit KeyClick(const DeviceConfig& config); ~KeyClick(); void reset(EmuTime::param time); void setClick(bool status, EmuTime::param time); private: const std::unique_ptr dac; bool status; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/YM2413.cc0000644000175000017500000000415612262345041017137 0ustar manuelmanuel00000000000000#include "YM2413.hh" #include "YM2413Okazaki.hh" #include "YM2413Burczynski.hh" #include "SimpleDebuggable.hh" #include "DeviceConfig.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { // YM2413Debuggable class YM2413Debuggable : public SimpleDebuggable { public: YM2413Debuggable(MSXMotherBoard& motherBoard, YM2413& ym2413); virtual byte read(unsigned address); virtual void write(unsigned address, byte value, EmuTime::param time); private: YM2413& ym2413; }; YM2413Debuggable::YM2413Debuggable( MSXMotherBoard& motherBoard, YM2413& ym2413_) : SimpleDebuggable(motherBoard, ym2413_.getName() + " regs", "MSX-MUSIC", 0x40) , ym2413(ym2413_) { } byte YM2413Debuggable::read(unsigned address) { return ym2413.core->peekReg(address); } void YM2413Debuggable::write(unsigned address, byte value, EmuTime::param time) { ym2413.writeReg(address, value, time); } // YM2413 static std::unique_ptr createCore(const DeviceConfig& config) { if (config.getChildDataAsBool("alternative", false)) { return make_unique(); } else { return make_unique(); } } YM2413::YM2413(const std::string& name, const DeviceConfig& config) : ResampledSoundDevice(config.getMotherBoard(), name, "MSX-MUSIC", 9 + 5) , core(createCore(config)) , debuggable(make_unique( config.getMotherBoard(), *this)) { double input = YM2413Core::CLOCK_FREQ / 72.0; setInputRate(int(input + 0.5)); registerSound(config); } YM2413::~YM2413() { unregisterSound(); } void YM2413::reset(EmuTime::param time) { updateStream(time); core->reset(); } void YM2413::writeReg(byte reg, byte value, EmuTime::param time) { updateStream(time); core->writeReg(reg, value); } void YM2413::generateChannels(int** bufs, unsigned num) { core->generateChannels(bufs, num); } int YM2413::getAmplificationFactor() const { return core->getAmplificationFactor(); } template void YM2413::serialize(Archive& ar, unsigned /*version*/) { ar.serializePolymorphic("ym2413", *core); } INSTANTIATE_SERIALIZE_METHODS(YM2413); } // namespace openmsx openmsx-0.10.0/src/sound/YM2151.hh0000644000175000017500000000344412262345041017147 0ustar manuelmanuel00000000000000/* ** ** File: ym2151.h - header file for software implementation of YM2151 ** FM Operator Type-M(OPM) ** ** (c) 1997-2002 Jarek Burczynski (s0246@poczta.onet.pl, bujar@mame.net) ** Some of the optimizing ideas by Tatsuyuki Satoh ** ** Version 2.150 final beta May, 11th 2002 ** ** ** I would like to thank following people for making this project possible: ** ** Beauty Planets - for making a lot of real YM2151 samples and providing ** additional informations about the chip. Also for the time spent making ** the samples and the speed of replying to my endless requests. ** ** Shigeharu Isoda - for general help, for taking time to scan his YM2151 ** Japanese Manual first of all, and answering MANY of my questions. ** ** Nao - for giving me some info about YM2151 and pointing me to Shigeharu. ** Also for creating fmemu (which I still use to test the emulator). ** ** Aaron Giles and Chris Hardy - they made some samples of one of my favourite ** arcade games so I could compare it to my emulator. ** ** Bryan McPhail and Tim (powerjaw) - for making some samples. ** ** Ishmair - for the datasheet and motivation. */ #ifndef YM2151_HH #define YM2151_HH #include "EmuTime.hh" #include "openmsx.hh" #include #include namespace openmsx { class MSXMotherBoard; class DeviceConfig; class YM2151 { public: YM2151(const std::string& name, const std::string& desc, const DeviceConfig& config, EmuTime::param time); ~YM2151(); void reset(EmuTime::param time); void writeReg(byte r, byte v, EmuTime::param time); byte readStatus() const; template void serialize(Archive& ar, unsigned version); private: class Impl; const std::unique_ptr pimpl; }; } // namespace openmsx #endif /*YM2151_HH*/ openmsx-0.10.0/src/sound/YM2413.hh0000644000175000017500000000150412262345041017143 0ustar manuelmanuel00000000000000#ifndef YM2413_HH #define YM2413_HH #include "ResampledSoundDevice.hh" #include "EmuTime.hh" #include "openmsx.hh" #include #include namespace openmsx { class YM2413Core; class YM2413Debuggable; class MSXMotherBoard; class YM2413 : public ResampledSoundDevice { public: YM2413(const std::string& name, const DeviceConfig& config); virtual ~YM2413(); void reset(EmuTime::param time); void writeReg(byte reg, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: // SoundDevice virtual void generateChannels(int** bufs, unsigned num); virtual int getAmplificationFactor() const; const std::unique_ptr core; const std::unique_ptr debuggable; friend class YM2413Debuggable; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/YM2413OkazakiTable.ii0000644000175000017500000005746512262345041021450 0ustar manuelmanuel00000000000000// This is a generated file. DO NOT EDIT! // These tables were generated for the following constants: static_assert(PM_FP_BITS == 8, "mismatch, regenerate"); static_assert(EP_FP_BITS == 15, "mismatch, regenerate"); static_assert(DB_BITS == 8, "mismatch, regenerate"); static_assert(DB_MUTE == 256, "mismatch, regenerate"); static_assert(DBTABLEN == 768, "mismatch, regenerate"); //static_assert(DB_STEP == 0.1875, "mismatch, regenerate"); //static_assert(EG_STEP == 0.375, "mismatch, regenerate"); static_assert(PG_BITS == 9, "mismatch, regenerate"); static_assert(PG_WIDTH == 512, "mismatch, regenerate"); static_assert(EG_BITS == 7, "mismatch, regenerate"); static_assert(DB2LIN_AMP_BITS == 8, "mismatch, regenerate"); static_assert(LFO_AM_TAB_ELEMENTS == 210, "mismatch, regenerate"); // LFO Phase Modulation table (copied from Burczynski core) static const signed char pmTable[8][8] = { { 0, 0, 0, 0, 0, 0, 0, 0, }, // FNUM = 000xxxxxx { 0, 0, 1, 0, 0, 0,-1, 0, }, // FNUM = 001xxxxxx { 0, 1, 2, 1, 0,-1,-2,-1, }, // FNUM = 010xxxxxx { 0, 1, 3, 1, 0,-1,-3,-1, }, // FNUM = 011xxxxxx { 0, 2, 4, 2, 0,-2,-4,-2, }, // FNUM = 100xxxxxx { 0, 2, 5, 2, 0,-2,-5,-2, }, // FNUM = 101xxxxxx { 0, 3, 6, 3, 0,-3,-6,-3, }, // FNUM = 110xxxxxx { 0, 3, 7, 3, 0,-3,-7,-3, }, // FNUM = 111xxxxxx }; // dB to linear table (used by Slot) // dB(0 .. DB_MUTE-1) -> linear(0 .. DB2LIN_AMP_WIDTH) // indices in range: // [0, DB_MUTE ) actual values, from max to min // [DB_MUTE, DBTABLEN) filled with min val (to allow some overflow in index) // [DBTABLEN, 2*DBTABLEN) as above but for negative output values static int dB2LinTab[2 * DBTABLEN] = { 255, 249, 244, 239, 233, 228, 224, 219, 214, 209, 205, 201, 196, 192, 188, 184, 180, 176, 172, 169, 165, 162, 158, 155, 151, 148, 145, 142, 139, 136, 133, 130, 127, 125, 122, 119, 117, 114, 112, 109, 107, 105, 102, 100, 98, 96, 94, 92, 90, 88, 86, 84, 82, 81, 79, 77, 76, 74, 72, 71, 69, 68, 66, 65, 64, 62, 61, 60, 58, 57, 56, 55, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 38, 37, 36, 35, 34, 34, 33, 32, 32, 31, 30, 30, 29, 28, 28, 27, 27, 26, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 19, 18, 18, 17, 17, 17, 16, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -255, -249, -244, -239, -233, -228, -224, -219, -214, -209, -205, -201, -196, -192, -188, -184, -180, -176, -172, -169, -165, -162, -158, -155, -151, -148, -145, -142, -139, -136, -133, -130, -127, -125, -122, -119, -117, -114, -112, -109, -107, -105, -102, -100, -98, -96, -94, -92, -90, -88, -86, -84, -82, -81, -79, -77, -76, -74, -72, -71, -69, -68, -66, -65, -64, -62, -61, -60, -58, -57, -56, -55, -53, -52, -51, -50, -49, -48, -47, -46, -45, -44, -43, -42, -41, -40, -39, -38, -38, -37, -36, -35, -34, -34, -33, -32, -32, -31, -30, -30, -29, -28, -28, -27, -27, -26, -25, -25, -24, -24, -23, -23, -22, -22, -21, -21, -20, -20, -19, -19, -19, -18, -18, -17, -17, -17, -16, -16, -16, -15, -15, -15, -14, -14, -14, -13, -13, -13, -12, -12, -12, -12, -11, -11, -11, -11, -10, -10, -10, -10, -10, -9, -9, -9, -9, -8, -8, -8, -8, -8, -8, -7, -7, -7, -7, -7, -7, -6, -6, -6, -6, -6, -6, -6, -5, -5, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // Linear to Log curve conversion table (for Attack rate) static unsigned AR_ADJUST_TABLE[1 << EG_BITS] = { 127, 127, 108, 98, 90, 84, 80, 75, 72, 69, 66, 64, 61, 59, 57, 56, 54, 52, 51, 49, 48, 47, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 36, 35, 34, 33, 33, 32, 31, 30, 30, 29, 29, 28, 27, 27, 26, 26, 25, 24, 24, 23, 23, 22, 22, 21, 21, 21, 20, 20, 19, 19, 18, 18, 17, 17, 17, 16, 16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, }; // KSL + TL Table values are in range [0, 112] static byte tllTable[4][16 * 8] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8, 0, 0, 0, 2, 4, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12, 0, 0, 4, 6, 8, 9, 10, 11, 12, 13, 13, 14, 14, 15, 15, 16, 0, 4, 8, 10, 12, 13, 14, 15, 16, 17, 17, 18, 18, 19, 19, 20, 0, 8, 12, 14, 16, 17, 18, 19, 20, 21, 21, 22, 22, 23, 23, 24, 0, 12, 16, 18, 20, 21, 22, 23, 24, 25, 25, 26, 26, 27, 27, 28 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 3, 5, 7, 8, 10, 11, 12, 13, 14, 15, 16, 0, 0, 0, 5, 8, 11, 13, 15, 16, 18, 19, 20, 21, 22, 23, 24, 0, 0, 8, 13, 16, 19, 21, 23, 24, 26, 27, 28, 29, 30, 31, 32, 0, 8, 16, 21, 24, 27, 29, 31, 32, 34, 35, 36, 37, 38, 39, 40, 0, 16, 24, 29, 32, 35, 37, 39, 40, 42, 43, 44, 45, 46, 47, 48, 0, 24, 32, 37, 40, 43, 45, 47, 48, 50, 51, 52, 53, 54, 55, 56 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 6, 8, 10, 12, 14, 16, 0, 0, 0, 0, 0, 6, 10, 14, 16, 20, 22, 24, 26, 28, 30, 32, 0, 0, 0, 10, 16, 22, 26, 30, 32, 36, 38, 40, 42, 44, 46, 48, 0, 0, 16, 26, 32, 38, 42, 46, 48, 52, 54, 56, 58, 60, 62, 64, 0, 16, 32, 42, 48, 54, 58, 62, 64, 68, 70, 72, 74, 76, 78, 80, 0, 32, 48, 58, 64, 70, 74, 78, 80, 84, 86, 88, 90, 92, 94, 96, 0, 48, 64, 74, 80, 86, 90, 94, 96,100,102,104,106,108,110,112 }, }; // WaveTable for each envelope amp // values are in range [0, DB_MUTE) (for positive values) // or [0, DB_MUTE) + DBTABLEN (for negative values) static unsigned fullsintable[PG_WIDTH] = { 255, 203, 171, 152, 139, 129, 120, 113, 107, 102, 97, 92, 88, 85, 81, 78, 75, 72, 70, 67, 65, 63, 61, 59, 57, 55, 53, 52, 50, 48, 47, 45, 44, 43, 41, 40, 39, 38, 37, 35, 34, 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 24, 23, 23, 22, 21, 21, 20, 19, 19, 18, 17, 17, 16, 16, 15, 14, 14, 13, 13, 12, 12, 11, 11, 11, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 26, 27, 28, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 43, 44, 45, 47, 48, 50, 52, 53, 55, 57, 59, 61, 63, 65, 67, 70, 72, 75, 78, 81, 85, 88, 92, 97, 102, 107, 113, 120, 129, 139, 152, 171, 203, 255, 1023, 971, 939, 920, 907, 897, 888, 881, 875, 870, 865, 860, 856, 853, 849, 846, 843, 840, 838, 835, 833, 831, 829, 827, 825, 823, 821, 820, 818, 816, 815, 813, 812, 811, 809, 808, 807, 806, 805, 803, 802, 801, 800, 799, 798, 797, 796, 796, 795, 794, 793, 792, 791, 791, 790, 789, 789, 788, 787, 787, 786, 785, 785, 784, 784, 783, 782, 782, 781, 781, 780, 780, 779, 779, 779, 778, 778, 777, 777, 776, 776, 776, 775, 775, 775, 774, 774, 774, 773, 773, 773, 772, 772, 772, 772, 771, 771, 771, 771, 770, 770, 770, 770, 770, 770, 769, 769, 769, 769, 769, 769, 769, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 768, 769, 769, 769, 769, 769, 769, 769, 770, 770, 770, 770, 770, 770, 771, 771, 771, 771, 772, 772, 772, 772, 773, 773, 773, 774, 774, 774, 775, 775, 775, 776, 776, 776, 777, 777, 778, 778, 779, 779, 779, 780, 780, 781, 781, 782, 782, 783, 784, 784, 785, 785, 786, 787, 787, 788, 789, 789, 790, 791, 791, 792, 793, 794, 795, 796, 796, 797, 798, 799, 800, 801, 802, 803, 805, 806, 807, 808, 809, 811, 812, 813, 815, 816, 818, 820, 821, 823, 825, 827, 829, 831, 833, 835, 838, 840, 843, 846, 849, 853, 856, 860, 865, 870, 875, 881, 888, 897, 907, 920, 939, 971, 1023, }; static unsigned halfsintable[PG_WIDTH] = { 255, 203, 171, 152, 139, 129, 120, 113, 107, 102, 97, 92, 88, 85, 81, 78, 75, 72, 70, 67, 65, 63, 61, 59, 57, 55, 53, 52, 50, 48, 47, 45, 44, 43, 41, 40, 39, 38, 37, 35, 34, 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 24, 23, 23, 22, 21, 21, 20, 19, 19, 18, 17, 17, 16, 16, 15, 14, 14, 13, 13, 12, 12, 11, 11, 11, 10, 10, 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19, 19, 20, 21, 21, 22, 23, 23, 24, 25, 26, 27, 28, 28, 29, 30, 31, 32, 33, 34, 35, 37, 38, 39, 40, 41, 43, 44, 45, 47, 48, 50, 52, 53, 55, 57, 59, 61, 63, 65, 67, 70, 72, 75, 78, 81, 85, 88, 92, 97, 102, 107, 113, 120, 129, 139, 152, 171, 203, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, }; static unsigned* waveform[2] = {fullsintable, halfsintable}; // Phase incr table for attack, decay and release // note: original code had indices swapped. It also had // a separate table for attack // 17.15 fixed point static int dphaseDRTable[16][16] = { { 0, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 }, { 0, 5, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240, 20480, 40960, 81920 }, { 0, 6, 12, 24, 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288, 24576, 49152, 98304 }, { 0, 7, 14, 28, 56, 112, 224, 448, 896, 1792, 3584, 7168, 14336, 28672, 57344,114688 }, { 0, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 65536 }, { 0, 10, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240, 20480, 40960, 81920, 81920 }, { 0, 12, 24, 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288, 24576, 49152, 98304, 98304 }, { 0, 14, 28, 56, 112, 224, 448, 896, 1792, 3584, 7168, 14336, 28672, 57344,114688,114688 }, { 0, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 65536, 65536 }, { 0, 20, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240, 20480, 40960, 81920, 81920, 81920 }, { 0, 24, 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288, 24576, 49152, 98304, 98304, 98304 }, { 0, 28, 56, 112, 224, 448, 896, 1792, 3584, 7168, 14336, 28672, 57344,114688,114688,114688 }, { 0, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 65536, 65536, 65536 }, { 0, 40, 80, 160, 320, 640, 1280, 2560, 5120, 10240, 20480, 40960, 81920, 81920, 81920, 81920 }, { 0, 48, 96, 192, 384, 768, 1536, 3072, 6144, 12288, 24576, 49152, 98304, 98304, 98304, 98304 }, { 0, 56, 112, 224, 448, 896, 1792, 3584, 7168, 14336, 28672, 57344,114688,114688,114688,114688 }, }; // LFO Amplitude Modulation table (verified on real YM3812) static const unsigned char lfo_am_table[LFO_AM_TAB_ELEMENTS] = { 0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25, 24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1, }; // Sustain level (17.15 fixed point) static const unsigned slTable[16] = { 0, 262144, 524288, 786432, 1048576, 1310720, 1572864, 1835008, 2097152, 2359296, 2621440, 2883584, 3145728, 3407872, 3670016, 4194304, }; // ML-table static const byte mlTable[16] = { 1, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2, 7*2, 8*2, 9*2, 10*2, 10*2, 12*2, 12*2, 15*2, 15*2 }; openmsx-0.10.0/src/sound/WavWriter.hh0000644000175000017500000000370112262345041020237 0ustar manuelmanuel00000000000000#ifndef WAVWRITER_HH #define WAVWRITER_HH #include "noncopyable.hh" #include #include namespace openmsx { class File; class Filename; /** Base class for writing WAV files. */ class WavWriter : private noncopyable { public: /** Returns false if there has been data written to the wav image. */ bool isEmpty() const; /** Flush data to file and update header. Try to make (possibly) * incomplete file already usable for external programs. */ void flush(); protected: WavWriter(const Filename& filename, unsigned channels, unsigned bits, unsigned frequency); ~WavWriter(); const std::unique_ptr file; unsigned bytes; }; /** Writes 8-bit WAV files. */ class Wav8Writer : public WavWriter { public: Wav8Writer(const Filename& filename, unsigned channels, unsigned frequency) : WavWriter(filename, channels, 8, frequency) {} void write(const unsigned char* buffer, unsigned stereo, unsigned samples) { assert(stereo == 1 || stereo == 2); write(buffer, stereo * samples); } private: void write(const unsigned char* buffer, unsigned samples); }; /** Writes 16-bit WAV files. */ class Wav16Writer : public WavWriter { public: Wav16Writer(const Filename& filename, unsigned channels, unsigned frequency) : WavWriter(filename, channels, 16, frequency) {} void write(const short* buffer, unsigned stereo, unsigned samples) { assert(stereo == 1 || stereo == 2); write(buffer, stereo * samples); } void write(const int* buffer, unsigned stereo, unsigned samples, int amp = 1) { assert(stereo == 1 || stereo == 2); write(buffer, stereo * samples, amp); } void writeSilence(unsigned stereo, unsigned samples) { assert(stereo == 1 || stereo == 2); writeSilence(stereo * samples); } private: void write(const short* buffer, unsigned samples); void write(const int* buffer, unsigned samples, int amp); void writeSilence(unsigned samples); }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/Y8950KeyboardConnector.cc0000644000175000017500000000270212262345041022365 0ustar manuelmanuel00000000000000#include "Y8950KeyboardConnector.hh" #include "Y8950KeyboardDevice.hh" #include "DummyY8950KeyboardDevice.hh" #include "checked_cast.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { Y8950KeyboardConnector::Y8950KeyboardConnector( PluggingController& pluggingController) : Connector(pluggingController, "audiokeyboardport", make_unique()) , data(255) { } Y8950KeyboardConnector::~Y8950KeyboardConnector() { } void Y8950KeyboardConnector::write(byte newData, EmuTime::param time) { if (newData != data) { data = newData; getPluggedKeyb().write(data, time); } } byte Y8950KeyboardConnector::read(EmuTime::param time) { return getPluggedKeyb().read(time); } const std::string Y8950KeyboardConnector::getDescription() const { return "MSX-AUDIO keyboard connector"; } string_ref Y8950KeyboardConnector::getClass() const { return "Y8950 Keyboard Port"; } void Y8950KeyboardConnector::plug(Pluggable& dev, EmuTime::param time) { Connector::plug(dev, time); getPluggedKeyb().write(data, time); } Y8950KeyboardDevice& Y8950KeyboardConnector::getPluggedKeyb() const { return *checked_cast(&getPlugged()); } template void Y8950KeyboardConnector::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); // don't serialize 'data', done in Y8950 } INSTANTIATE_SERIALIZE_METHODS(Y8950KeyboardConnector); } // namespace openmsx openmsx-0.10.0/src/sound/Mixer.hh0000644000175000017500000000315012262345041017367 0ustar manuelmanuel00000000000000#ifndef MIXER_HH #define MIXER_HH #include "Observer.hh" #include "noncopyable.hh" #include #include namespace openmsx { class SoundDriver; class Reactor; class CommandController; class MSXMixer; class IntegerSetting; class BooleanSetting; template class EnumSetting; class Setting; class Mixer : private Observer, private noncopyable { public: Mixer(Reactor& reactor, CommandController& commandController); virtual ~Mixer(); /** Register per-machine mixer */ void registerMixer(MSXMixer& mixer); /** Unregister per-machine mixer */ void unregisterMixer(MSXMixer& mixer); /** * This methods (un)mute the sound. * These methods may be called multiple times, as long as * you never call unmute() more than mute() */ void mute(); void unmute(); // Called by MSXMixer /** Upload new sample data */ void uploadBuffer(MSXMixer& msxMixer, short* buffer, unsigned len); IntegerSetting& getMasterVolume() const; private: void reloadDriver(); void muteHelper(); // Observer virtual void update(const Setting& setting); std::vector msxMixers; std::unique_ptr driver; Reactor& reactor; CommandController& commandController; const std::unique_ptr muteSetting; const std::unique_ptr masterVolume; const std::unique_ptr frequencySetting; const std::unique_ptr samplesSetting; enum SoundDriverType { SND_NULL, SND_SDL, SND_DIRECTX, SND_LIBAO }; std::unique_ptr> soundDriverSetting; int muteCount; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/ResampledSoundDevice.hh0000644000175000017500000000231712262345041022354 0ustar manuelmanuel00000000000000#ifndef RESAMPLEDSOUNDDEVICE_HH #define RESAMPLEDSOUNDDEVICE_HH #include "SoundDevice.hh" #include "Observer.hh" #include namespace openmsx { class MSXMotherBoard; class ResampleAlgo; class Setting; template class EnumSetting; class ResampledSoundDevice : public SoundDevice, protected Observer { public: enum ResampleType { RESAMPLE_HQ, RESAMPLE_LQ, RESAMPLE_BLIP }; /** Note: To enable various optimizations (like SSE), this method is * allowed to generate up to 3 extra sample. * @see SoundDevice::updateBuffer() */ bool generateInput(int* buffer, unsigned num); protected: ResampledSoundDevice(MSXMotherBoard& motherBoard, string_ref name, string_ref description, unsigned channels, bool stereo = false); virtual ~ResampledSoundDevice(); // SoundDevice virtual void setOutputRate(unsigned sampleRate); virtual bool updateBuffer(unsigned length, int* buffer, EmuTime::param time); // Observer virtual void update(const Setting& setting); void createResampler(); private: EnumSetting& resampleSetting; std::unique_ptr algo; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/Mixer.cc0000644000175000017500000001100212262345041017350 0ustar manuelmanuel00000000000000#include "Mixer.hh" #include "MSXMixer.hh" #include "NullSoundDriver.hh" #include "SDLSoundDriver.hh" #include "DirectXSoundDriver.hh" #include "LibAOSoundDriver.hh" #include "CommandController.hh" #include "CliComm.hh" #include "IntegerSetting.hh" #include "BooleanSetting.hh" #include "EnumSetting.hh" #include "MSXException.hh" #include "unreachable.hh" #include "memory.hh" #include "components.hh" #include "build-info.hh" #include namespace openmsx { #if defined(_WIN32) static const int defaultsamples = 2048; #elif PLATFORM_ANDROID static const int defaultsamples = 2560; #else static const int defaultsamples = 1024; #endif Mixer::Mixer(Reactor& reactor_, CommandController& commandController_) : reactor(reactor_) , commandController(commandController_) , muteSetting(make_unique( commandController, "mute", "(un)mute the emulation sound", false, Setting::DONT_SAVE)) , masterVolume(make_unique( commandController, "master_volume", "master volume", 75, 0, 100)) , frequencySetting(make_unique( commandController, "frequency", "mixer frequency", 44100, 11025, 48000)) , samplesSetting(make_unique( commandController, "samples", "mixer samples", defaultsamples, 64, 8192)) , muteCount(0) { EnumSetting::Map soundDriverMap; soundDriverMap.push_back(std::make_pair("null", SND_NULL)); soundDriverMap.push_back(std::make_pair("sdl", SND_SDL)); SoundDriverType defaultSoundDriver = SND_SDL; #ifdef _WIN32 soundDriverMap.push_back(std::make_pair("directx", SND_DIRECTX)); defaultSoundDriver = SND_DIRECTX; #endif #if COMPONENT_AO soundDriverMap.push_back(std::make_pair("libao", SND_LIBAO)); #endif soundDriverSetting = make_unique>( commandController, "sound_driver", "select the sound output driver", defaultSoundDriver, soundDriverMap); muteSetting->attach(*this); frequencySetting->attach(*this); samplesSetting->attach(*this); soundDriverSetting->attach(*this); // Set correct initial mute state. if (muteSetting->getBoolean()) ++muteCount; reloadDriver(); } Mixer::~Mixer() { assert(msxMixers.empty()); driver.reset(); soundDriverSetting->detach(*this); samplesSetting->detach(*this); frequencySetting->detach(*this); muteSetting->detach(*this); } void Mixer::reloadDriver() { driver = make_unique(); try { switch (soundDriverSetting->getEnum()) { case SND_NULL: driver = make_unique(); break; case SND_SDL: driver = make_unique( reactor, frequencySetting->getInt(), samplesSetting->getInt()); break; #ifdef _WIN32 case SND_DIRECTX: driver = make_unique( frequencySetting->getInt(), samplesSetting->getInt()); break; #endif #if COMPONENT_AO case SND_LIBAO: driver = make_unique( frequencySetting->getInt(), samplesSetting->getInt()); break; #endif default: UNREACHABLE; } } catch (MSXException& e) { commandController.getCliComm().printWarning(e.getMessage()); } muteHelper(); } void Mixer::registerMixer(MSXMixer& mixer) { assert(count(msxMixers.begin(), msxMixers.end(), &mixer) == 0); msxMixers.push_back(&mixer); muteHelper(); } void Mixer::unregisterMixer(MSXMixer& mixer) { assert(count(msxMixers.begin(), msxMixers.end(), &mixer) == 1); msxMixers.erase(find(msxMixers.begin(), msxMixers.end(), &mixer)); muteHelper(); } void Mixer::mute() { if (muteCount++ == 0) { muteHelper(); } } void Mixer::unmute() { assert(muteCount); if (--muteCount == 0) { muteHelper(); } } void Mixer::muteHelper() { bool mute = muteCount || msxMixers.empty(); unsigned samples = mute ? 0 : driver->getSamples(); unsigned frequency = driver->getFrequency(); for (auto& m : msxMixers) { m->setMixerParams(samples, frequency); } if (mute) { driver->mute(); } else { driver->unmute(); } } IntegerSetting& Mixer::getMasterVolume() const { return *masterVolume; } void Mixer::uploadBuffer(MSXMixer& /*msxMixer*/, short* buffer, unsigned len) { // can only handle one MSXMixer ATM assert(!msxMixers.empty()); driver->uploadBuffer(buffer, len); } void Mixer::update(const Setting& setting) { if (&setting == muteSetting.get()) { if (muteSetting->getBoolean()) { mute(); } else { unmute(); } } else if ((&setting == samplesSetting.get()) || (&setting == soundDriverSetting.get()) || (&setting == frequencySetting.get())) { reloadDriver(); } else { UNREACHABLE; } } } // namespace openmsx openmsx-0.10.0/src/sound/MSXMoonSound.cc0000644000175000017500000002073512262345041020612 0ustar manuelmanuel00000000000000// ATM this class does several things: // - It connects the YMF278b chip to specific I/O ports in the MSX machine // - It glues the YMF262 (FM-part) and YMF278 (Wave-part) classes together in a // full model of a YMF278b chip. IOW part of the logic of the YM278b is // modeled here instead of in a chip-specific class. // TODO it would be nice to move the functionality of the 2nd point to a // different class, but until there's a 2nd user of this chip, this is // low priority. #include "MSXMoonSound.hh" #include "YMF262.hh" #include "YMF278.hh" #include "Clock.hh" #include "serialize.hh" #include "unreachable.hh" #include "memory.hh" namespace openmsx { // The master clock, running at 33.8MHz. typedef Clock<33868800> MasterClock; // Required delay between register select and register read/write. static const EmuDuration FM_REG_SELECT_DELAY = MasterClock::duration(56); // Required delay after register write. static const EmuDuration FM_REG_WRITE_DELAY = MasterClock::duration(56); // Datasheet doesn't mention any delay for reads from the FM registers. In fact // it says reads from FM registers are not possible while tests on a real // YMF278 show they do work (value of the NEW2 bit doesn't matter). // Required delay between register select and register read/write. static const EmuDuration WAVE_REG_SELECT_DELAY = MasterClock::duration(88); // Required delay after register write. static const EmuDuration WAVE_REG_WRITE_DELAY = MasterClock::duration(88); // Datasheet doesn't mention any delay for register reads (except for reads // from register 6, see below). I also couldn't measure any delay on a real // YMF278. // Required delay after memory read. static const EmuDuration MEM_READ_DELAY = MasterClock::duration(38); // Required delay after memory write (instead of register write delay). static const EmuDuration MEM_WRITE_DELAY = MasterClock::duration(28); // Required delay after instrument load. // We pick 10000 cycles, this is approximately 300us (the number given in the // datasheet). The exact number of cycles is unknown. But I did some (very // rough) tests on real HW, and this number is not too bad (slightly too high // but within 2%-4% of real value, needs more detailed tests). static const EmuDuration LOAD_DELAY = MasterClock::duration(10000); MSXMoonSound::MSXMoonSound(const DeviceConfig& config) : MSXDevice(config) , ymf262(make_unique(getName() + " FM", config, true)) , ymf278(make_unique( getName() + " wave", config.getChildDataAsInt("sampleram", 512), // size in kb config)) , ymf278LoadTime(getCurrentTime()) , ymf278BusyTime(getCurrentTime()) { powerUp(getCurrentTime()); } MSXMoonSound::~MSXMoonSound() { } void MSXMoonSound::powerUp(EmuTime::param time) { ymf278->clearRam(); reset(time); } void MSXMoonSound::reset(EmuTime::param time) { ymf262->reset(time); ymf278->reset(time); opl4latch = 0; // TODO check opl3latch = 0; // TODO check alreadyReadID = false; ymf278BusyTime = time; ymf278LoadTime = time; } byte MSXMoonSound::readIO(word port, EmuTime::param time) { byte result; if ((port & 0xFF) < 0xC0) { // WAVE part 0x7E-0x7F switch (port & 0x01) { case 0: // read latch, not supported result = 255; break; case 1: // read wave register // Verified on real YMF278: // Even if NEW2=0 reads happen normally. Also reads // from sample memory (and thus the internal memory // pointer gets increased). if ((3 <= opl4latch) && (opl4latch <= 6)) { // This time is so small that on a MSX you can // never see BUSY=1. So I also couldn't test // whether this timing applies to registers 3-6 // (like for write) or only to register 6. I // also couldn't test how the other registers // behave. // TODO Should we comment out this code? It // doesn't have any measurable effect on MSX. ymf278BusyTime = time + MEM_READ_DELAY; } result = ymf278->readReg(opl4latch); break; default: // unreachable, avoid warning UNREACHABLE; result = 255; } } else { // FM part 0xC4-0xC7 switch (port & 0x03) { case 0: // read status case 2: result = ymf262->readStatus() | readYMF278Status(time); if (!alreadyReadID && getNew2()) { // Verified on real YMF278: // Only once after switching NEW2=1, reading // the status register returns '0x02'. This // behavior doesn't re-occur till after a // reset (datasheet confirms this behavior). // Also verified that only bit 1 changes (so // it's not the whole value that is forced to // 0x02, datasheet isn't clear about that). alreadyReadID = true; result |= 0x02; } break; case 1: case 3: // read fm register result = ymf262->readReg(opl3latch); break; default: // unreachable, avoid warning UNREACHABLE; result = 255; } } return result; } byte MSXMoonSound::peekIO(word port, EmuTime::param time) const { byte result; if ((port & 0xFF) < 0xC0) { // WAVE part 0x7E-0x7F switch (port & 0x01) { case 0: // read latch, not supported result = 255; break; case 1: // read wave register result = ymf278->peekReg(opl4latch); break; default: // unreachable, avoid warning UNREACHABLE; result = 255; } } else { // FM part 0xC4-0xC7 switch (port & 0x03) { case 0: // read status case 2: result = ymf262->peekStatus() | readYMF278Status(time); if (!alreadyReadID && getNew2()) { result |= 0x02; } break; case 1: case 3: // read fm register result = ymf262->peekReg(opl3latch); break; default: // unreachable, avoid warning UNREACHABLE; result = 255; } } return result; } void MSXMoonSound::writeIO(word port, byte value, EmuTime::param time) { if ((port & 0xFF) < 0xC0) { // WAVE part 0x7E-0x7F if (getNew2()) { switch (port & 0x01) { case 0: // select register ymf278BusyTime = time + WAVE_REG_SELECT_DELAY; opl4latch = value; break; case 1: if ((0x08 <= opl4latch) && (opl4latch <= 0x1F)) { ymf278LoadTime = time + LOAD_DELAY; } if ((3 <= opl4latch) && (opl4latch <= 6)) { // Note: this time is so small that on // MSX you never see BUSY=1 for these // registers. Confirmed on real HW that // also registers 3-5 are faster. ymf278BusyTime = time + MEM_WRITE_DELAY; } else { // For the other registers it is // possible to see BUSY=1, but only // very briefly and only on R800. ymf278BusyTime = time + WAVE_REG_WRITE_DELAY; } ymf278->writeReg(opl4latch, value, time); break; default: UNREACHABLE; } } else { // Verified on real YMF278: // Writes are ignored when NEW2=0 (both register select // and register write). } } else { // FM part 0xC4-0xC7 switch (port & 0x03) { case 0: // select register bank 0 opl3latch = value; ymf278BusyTime = time + FM_REG_SELECT_DELAY; break; case 2: // select register bank 1 opl3latch = value | 0x100; ymf278BusyTime = time + FM_REG_SELECT_DELAY; break; case 1: case 3: // write fm register ymf278BusyTime = time + FM_REG_WRITE_DELAY; ymf262->writeReg(opl3latch, value, time); break; default: UNREACHABLE; } } } bool MSXMoonSound::getNew2() const { return (ymf262->peekReg(0x105) & 0x02) != 0; } byte MSXMoonSound::readYMF278Status(EmuTime::param time) const { byte result = 0; if (time < ymf278BusyTime) result |= 0x01; if (time < ymf278LoadTime) result |= 0x02; return result; } // version 1: initial version // version 2: added alreadyReadID // version 3: moved loadTime and busyTime from YMF278 to here template void MSXMoonSound::serialize(Archive& ar, unsigned version) { ar.template serializeBase(*this); ar.serialize("ymf262", *ymf262); ar.serialize("ymf278", *ymf278); ar.serialize("opl3latch", opl3latch); ar.serialize("opl4latch", opl4latch); if (ar.versionAtLeast(version, 2)) { ar.serialize("alreadyReadID", alreadyReadID); } else { assert(ar.isLoader()); alreadyReadID = true; // we can't know the actual value, but // 'true' is the safest value } if (ar.versionAtLeast(version, 3)) { ar.serialize("loadTime", ymf278LoadTime); ar.serialize("busyTime", ymf278BusyTime); } else { assert(ar.isLoader()); // For 100% backwards compatibility we should restore these two // from the (old) YMF278 class. Though that's a lot of extra // work for very little gain. ymf278LoadTime = getCurrentTime(); ymf278BusyTime = getCurrentTime(); } } INSTANTIATE_SERIALIZE_METHODS(MSXMoonSound); REGISTER_MSXDEVICE(MSXMoonSound, "MoonSound"); } // namespace openmsx openmsx-0.10.0/src/sound/node.mk0000644000175000017500000000205112262345041017237 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ Mixer MSXMixer NullSoundDriver \ SDLSoundDriver DirectXSoundDriver \ SoundDevice ResampledSoundDevice \ ResampleTrivial ResampleHQ ResampleLQ ResampleBlip \ BlipBuffer \ MSXPSG AY8910 AY8910Periphery \ DACSound16S DACSound8U \ KeyClick \ SCC MSXSCCPlusCart \ VLM5030 \ MSXAudio \ EmuTimer \ Y8950 Y8950Adpcm Y8950KeyboardConnector \ Y8950Periphery \ Y8950KeyboardDevice DummyY8950KeyboardDevice \ MSXFmPac MSXMusic \ YM2413 YM2413Okazaki YM2413Burczynski \ MSXTurboRPCM \ YMF262 YMF278 MSXMoonSound MSXOPL3Cartridge \ YM2151 MSXYamahaSFG \ AudioInputConnector AudioInputDevice \ DummyAudioInputDevice WavAudioInput \ WavWriter \ SamplePlayer \ WavData HDR_ONLY:= \ SoundDriver \ ResampleAlgo \ BlipConfig \ YM2413Core \ YM2413OkazakiConfig \ DummyAY8910Periphery SRC_HDR_$(COMPONENT_AO)+= \ LibAOSoundDriver DIST:= \ ResampleCoeffs.ii \ BlipTable.ii \ YM2413OkazakiTable.ii \ generateBlipTable.cc \ generateYM2413OkazakiTable.cc \ #TODO #TEST:= YM2413Test include build/node-end.mk openmsx-0.10.0/src/sound/MSXTurboRPCM.cc0000644000175000017500000000721612262345041020445 0ustar manuelmanuel00000000000000#include "MSXTurboRPCM.hh" #include "MSXMotherBoard.hh" #include "AudioInputConnector.hh" #include "DACSound8U.hh" #include "MSXMixer.hh" #include "serialize.hh" #include "unreachable.hh" #include "memory.hh" namespace openmsx { MSXTurboRPCM::MSXTurboRPCM(const DeviceConfig& config) : MSXDevice(config) , mixer(getMotherBoard().getMSXMixer()) , connector(make_unique( getPluggingController(), "pcminput")) , dac(make_unique("PCM", "Turbo-R PCM", config)) , reference(getCurrentTime()) , hwMute(false) { reset(getCurrentTime()); } MSXTurboRPCM::~MSXTurboRPCM() { hardwareMute(false); } void MSXTurboRPCM::reset(EmuTime::param time) { reference.reset(time); status = 0; DValue = 0x80; // TODO correct initial value? hold = 0x80; // avoid UMR dac->reset(time); hardwareMute(false); } byte MSXTurboRPCM::readIO(word port, EmuTime::param time) { return peekIO(port, time); } byte MSXTurboRPCM::peekIO(word port, EmuTime::param time) const { byte result; switch (port & 0x01) { case 0: // bit 0-1 15.75kHz counter // bit 2-7 not used result = reference.getTicksTill(time) & 0x03; break; case 1: // bit 0 BUFF 0->D/A TODO check this bit // 1->A/D // bit 1 MUTE mute ALL sound 0->muted // bit 2 FILT filter 0->standard signal // 1->filtered signal // bit 3 SEL select 0->D/A // 1->Mic/Jack // bit 4 SMPL sample/hold 0->sample // 1->hold // bit 5-6 not used // bit 7 COMP comparator result 0->greater // 1->smaller result = (getComp(time) ? 0x80 : 0x00) | (status & 0x1F); break; default: // unreachable, avoid warning UNREACHABLE; result = 0; } return result; } void MSXTurboRPCM::writeIO(word port, byte value, EmuTime::param time) { switch (port & 0x01) { case 0: // While playing: sample value // recording: compare value // Resets counter reference.advance(time); DValue = value; if (status & 0x02) { dac->writeDAC(DValue, time); } break; case 1: // bit 0 BUFF // bit 1 MUTE mute _all_ sound 0->no sound // bit 2 FILT filter 1->filter on // bit 3 SEL select 1->Mic/Jack 0->D/A // bit 4 SMPL sample/hold 1->hold // bit 5-7 not used byte change = status ^ value; status = value; if ((change & 0x01) && ((status & 0x01) == 0)) { dac->writeDAC(DValue, time); } // TODO status & 0x08 if ((change & 0x10) && (status & 0x10)) { hold = getSample(time); } hardwareMute(!(status & 0x02)); break; } } byte MSXTurboRPCM::getSample(EmuTime::param time) const { byte result; if (status & 0x04) { result = (connector->readSample(time) / 256) + 0x80; } else { result = 0x80; // TODO check } //PRT_DEBUG("PCM: read " << (int)result); return result; } bool MSXTurboRPCM::getComp(EmuTime::param time) const { // TODO also when D/A ?? byte sample = (status & 0x10) ? hold : getSample(time); return sample >= DValue; } void MSXTurboRPCM::hardwareMute(bool mute) { if (mute == hwMute) return; hwMute = mute; if (hwMute) { mixer.mute(); } else { mixer.unmute(); } } template void MSXTurboRPCM::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("audioConnector", *connector); ar.serialize("reference", reference); ar.serialize("status", status); ar.serialize("DValue", DValue); ar.serialize("hold", hold); ar.serialize("DAC", *dac); hardwareMute(!(status & 0x02)); // restore hwMute } INSTANTIATE_SERIALIZE_METHODS(MSXTurboRPCM); REGISTER_MSXDEVICE(MSXTurboRPCM, "TurboR-PCM"); } // namespace openmsx openmsx-0.10.0/src/sound/Y8950KeyboardConnector.hh0000644000175000017500000000136112262345041022377 0ustar manuelmanuel00000000000000#ifndef Y8950KEYBOARDCONNECTOR_HH #define Y8950KEYBOARDCONNECTOR_HH #include "Connector.hh" #include "openmsx.hh" namespace openmsx { class Y8950KeyboardDevice; class Y8950KeyboardConnector : public Connector { public: explicit Y8950KeyboardConnector(PluggingController& pluggingController); virtual ~Y8950KeyboardConnector(); void write(byte data, EmuTime::param time); byte read(EmuTime::param time); Y8950KeyboardDevice& getPluggedKeyb() const; // Connector virtual const std::string getDescription() const; virtual string_ref getClass() const; virtual void plug(Pluggable& dev, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte data; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/YMF278.cc0000644000175000017500000010051112262345041017164 0ustar manuelmanuel00000000000000// Based on ymf278b.c written by R. Belmont and O. Galibert // This class doesn't model a full YMF278b chip. Instead it only models the // wave part. The FM part in modeled in YMF262 (it's almost 100% compatible, // the small differences are handled in YMF262). The status register and // interaction with the FM registers (e.g. the NEW2 bit) is currently handled // in the MSXMoonSound class. #include "YMF278.hh" #include "ResampledSoundDevice.hh" #include "Rom.hh" #include "SimpleDebuggable.hh" #include "DeviceConfig.hh" #include "MSXMotherBoard.hh" #include "MemBuffer.hh" #include "MSXException.hh" #include "StringOp.hh" #include "serialize.hh" #include "likely.hh" #include "memory.hh" #include #include #include namespace openmsx { class DebugRegisters : public SimpleDebuggable { public: DebugRegisters(YMF278& ymf278, MSXMotherBoard& motherBoard, const std::string& name); virtual byte read(unsigned address); virtual void write(unsigned address, byte value, EmuTime::param time); private: YMF278& ymf278; }; class DebugMemory : public SimpleDebuggable { public: DebugMemory(YMF278& ymf278, MSXMotherBoard& motherBoard, const std::string& name); virtual byte read(unsigned address); virtual void write(unsigned address, byte value); private: YMF278& ymf278; }; class YMF278Slot { public: YMF278Slot(); void reset(); int compute_rate(int val) const; unsigned decay_rate(int num, int sample_rate); void envelope_next(int sample_rate); inline int compute_vib() const; inline int compute_am() const; void set_lfo(int newlfo); template void serialize(Archive& ar, unsigned version); unsigned startaddr; unsigned loopaddr; unsigned endaddr; unsigned step; // fixed-point frequency step // invariant: step == calcStep(OCT, FN) unsigned stepptr; // fixed-point pointer into the sample unsigned pos; short sample1, sample2; int env_vol; int lfo_cnt; int lfo_step; int lfo_max; int DL; short wave; // wavetable number short FN; // f-number TODO store 'FN | 1024'? char OCT; // octave [0..15] TODO store sign-extended? char PRVB; // pseudo-reverb char LD; // level direct char TL; // total level char pan; // panpot char lfo; // LFO char vib; // vibrato char AM; // AM level char AR; char D1R; char D2R; char RC; // rate correction char RR; byte bits; // width of the samples bool active; // slot keyed on byte state; bool lfo_active; }; SERIALIZE_CLASS_VERSION(YMF278Slot, 3); class YMF278::Impl : public ResampledSoundDevice { public: Impl(YMF278& self, const std::string& name, int ramSize, const DeviceConfig& config); virtual ~Impl(); void clearRam(); void reset(EmuTime::param time); void writeReg(byte reg, byte data, EmuTime::param time); byte readReg(byte reg); byte peekReg(byte reg) const; byte readMem(unsigned address) const; void writeMem(unsigned address, byte value); template void serialize(Archive& ar, unsigned version); private: // SoundDevice virtual void generateChannels(int** bufs, unsigned num); void writeRegDirect(byte reg, byte data, EmuTime::param time); unsigned getRamAddress(unsigned addr) const; short getSample(YMF278Slot& op); void advance(); bool anyActive(); void keyOnHelper(YMF278Slot& slot); MSXMotherBoard& motherBoard; const std::unique_ptr debugRegisters; const std::unique_ptr debugMemory; YMF278Slot slots[24]; /** Global envelope generator counter. */ unsigned eg_cnt; int memadr; int fm_l, fm_r; int pcm_l, pcm_r; const std::unique_ptr rom; MemBuffer ram; /** Precalculated attenuation values with some margin for * envelope and pan levels. */ int volume[256 * 4]; byte regs[256]; }; // Don't set SERIALIZE_CLASS_VERSION on YMF278::Impl, instead set it on YMF278. static const int EG_SH = 16; // 16.16 fixed point (EG timing) static const unsigned EG_TIMER_OVERFLOW = 1 << EG_SH; // envelope output entries static const int ENV_BITS = 10; static const int ENV_LEN = 1 << ENV_BITS; static const double ENV_STEP = 128.0 / ENV_LEN; static const int MAX_ATT_INDEX = (1 << (ENV_BITS - 1)) - 1; // 511 static const int MIN_ATT_INDEX = 0; // Envelope Generator phases static const int EG_ATT = 4; static const int EG_DEC = 3; static const int EG_SUS = 2; static const int EG_REL = 1; static const int EG_OFF = 0; static const int EG_REV = 5; // pseudo reverb static const int EG_DMP = 6; // damp // Pan values, units are -3dB, i.e. 8. static const int pan_left[16] = { 0, 8, 16, 24, 32, 40, 48, 256, 256, 0, 0, 0, 0, 0, 0, 0 }; static const int pan_right[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 256, 256, 48, 40, 32, 24, 16, 8 }; // Mixing levels, units are -3dB, and add some marging to avoid clipping static const int mix_level[8] = { 8, 16, 24, 32, 40, 48, 56, 256 }; // decay level table (3dB per step) // 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB) #define SC(db) unsigned(db * (2.0 / ENV_STEP)) static const unsigned dl_tab[16] = { SC( 0), SC( 1), SC( 2), SC(3 ), SC(4 ), SC(5 ), SC(6 ), SC( 7), SC( 8), SC( 9), SC(10), SC(11), SC(12), SC(13), SC(14), SC(31) }; #undef SC static const byte RATE_STEPS = 8; static const byte eg_inc[15 * RATE_STEPS] = { //cycle:0 1 2 3 4 5 6 7 0, 1, 0, 1, 0, 1, 0, 1, // 0 rates 00..12 0 (increment by 0 or 1) 0, 1, 0, 1, 1, 1, 0, 1, // 1 rates 00..12 1 0, 1, 1, 1, 0, 1, 1, 1, // 2 rates 00..12 2 0, 1, 1, 1, 1, 1, 1, 1, // 3 rates 00..12 3 1, 1, 1, 1, 1, 1, 1, 1, // 4 rate 13 0 (increment by 1) 1, 1, 1, 2, 1, 1, 1, 2, // 5 rate 13 1 1, 2, 1, 2, 1, 2, 1, 2, // 6 rate 13 2 1, 2, 2, 2, 1, 2, 2, 2, // 7 rate 13 3 2, 2, 2, 2, 2, 2, 2, 2, // 8 rate 14 0 (increment by 2) 2, 2, 2, 4, 2, 2, 2, 4, // 9 rate 14 1 2, 4, 2, 4, 2, 4, 2, 4, // 10 rate 14 2 2, 4, 4, 4, 2, 4, 4, 4, // 11 rate 14 3 4, 4, 4, 4, 4, 4, 4, 4, // 12 rates 15 0, 15 1, 15 2, 15 3 for decay 8, 8, 8, 8, 8, 8, 8, 8, // 13 rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) 0, 0, 0, 0, 0, 0, 0, 0, // 14 infinity rates for attack and decay(s) }; #define O(a) (a * RATE_STEPS) static const byte eg_rate_select[64] = { O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 4),O( 5),O( 6),O( 7), O( 8),O( 9),O(10),O(11), O(12),O(12),O(12),O(12), }; #undef O // rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 // mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 #define O(a) (a) static const byte eg_rate_shift[64] = { O(12),O(12),O(12),O(12), O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0), }; #undef O // number of steps to take in quarter of lfo frequency // TODO check if frequency matches real chip #define O(a) int((EG_TIMER_OVERFLOW / a) / 6) static const int lfo_period[8] = { O(0.168), O(2.019), O(3.196), O(4.206), O(5.215), O(5.888), O(6.224), O(7.066) }; #undef O #define O(a) int(a * 65536) static const int vib_depth[8] = { O(0), O(3.378), O(5.065), O(6.750), O(10.114), O(20.170), O(40.106), O(79.307) }; #undef O #define SC(db) int(db * (2.0 / ENV_STEP)) static const int am_depth[8] = { SC(0), SC(1.781), SC(2.906), SC(3.656), SC(4.406), SC(5.906), SC(7.406), SC(11.91) }; #undef SC YMF278Slot::YMF278Slot() { reset(); } // Sign extend a 4-bit value to int (32-bit) // require: x in range [0..15] static inline int sign_extend_4(int x) { return (x ^ 8) - 8; } // Params: oct in [0 .. 15] // fn in [0 .. 1023] // We want to interpret oct as a signed 4-bit number and calculate // ((fn | 1024) + vib) << (5 + sign_extend_4(oct)) // Though in this formula the shift can go over a negative distance (in that // case we should shift in the other direction). static inline unsigned calcStep(unsigned oct, unsigned fn, unsigned vib = 0) { oct ^= 8; // [0..15] -> [8..15][0..7] == sign_extend_4(x) + 8 unsigned t = (fn + 1024 + vib) << oct; // use '+' iso '|' (generates slightly better code) return t >> 3; // was shifted 3 positions too far } void YMF278Slot::reset() { wave = FN = OCT = PRVB = LD = TL = pan = lfo = vib = AM = 0; AR = D1R = DL = D2R = RC = RR = 0; stepptr = 0; step = calcStep(OCT, FN); bits = startaddr = loopaddr = endaddr = 0; env_vol = MAX_ATT_INDEX; lfo_active = false; lfo_cnt = lfo_step = 0; lfo_max = lfo_period[0]; state = EG_OFF; active = false; // not strictly needed, but avoid UMR on savestate pos = sample1 = sample2 = 0; } int YMF278Slot::compute_rate(int val) const { if (val == 0) { return 0; } else if (val == 15) { return 63; } int res; if (RC != 15) { // TODO it may be faster to store 'OCT' sign extended int oct = sign_extend_4(OCT); res = (oct + RC) * 2 + (FN & 0x200 ? 1 : 0) + val * 4; } else { res = val * 4; } if (res < 0) { res = 0; } else if (res > 63) { res = 63; } return res; } int YMF278Slot::compute_vib() const { return (((lfo_step << 8) / lfo_max) * vib_depth[int(vib)]) >> 24; } int YMF278Slot::compute_am() const { if (lfo_active && AM) { return (((lfo_step << 8) / lfo_max) * am_depth[int(AM)]) >> 12; } else { return 0; } } void YMF278Slot::set_lfo(int newlfo) { lfo_step = (((lfo_step << 8) / lfo_max) * newlfo) >> 8; lfo_cnt = (((lfo_cnt << 8) / lfo_max) * newlfo) >> 8; lfo = newlfo; lfo_max = lfo_period[int(lfo)]; } void YMF278::Impl::advance() { eg_cnt++; for (int i = 0; i < 24; ++i) { YMF278Slot& op = slots[i]; if (op.lfo_active) { op.lfo_cnt++; if (op.lfo_cnt < op.lfo_max) { op.lfo_step++; } else if (op.lfo_cnt < (op.lfo_max * 3)) { op.lfo_step--; } else { op.lfo_step++; if (op.lfo_cnt == (op.lfo_max * 4)) { op.lfo_cnt = 0; } } } // Envelope Generator switch(op.state) { case EG_ATT: { // attack phase byte rate = op.compute_rate(op.AR); if (rate < 4) { break; } byte shift = eg_rate_shift[rate]; if (!(eg_cnt & ((1 << shift) -1))) { byte select = eg_rate_select[rate]; op.env_vol += (~op.env_vol * eg_inc[select + ((eg_cnt >> shift) & 7)]) >> 3; if (op.env_vol <= MIN_ATT_INDEX) { op.env_vol = MIN_ATT_INDEX; if (op.DL) { op.state = EG_DEC; } else { op.state = EG_SUS; } } } break; } case EG_DEC: { // decay phase byte rate = op.compute_rate(op.D1R); if (rate < 4) { break; } byte shift = eg_rate_shift[rate]; if (!(eg_cnt & ((1 << shift) -1))) { byte select = eg_rate_select[rate]; op.env_vol += eg_inc[select + ((eg_cnt >> shift) & 7)]; if ((unsigned(op.env_vol) > dl_tab[6]) && op.PRVB) { op.state = EG_REV; } else { if (op.env_vol >= op.DL) { op.state = EG_SUS; } } } break; } case EG_SUS: { // sustain phase byte rate = op.compute_rate(op.D2R); if (rate < 4) { break; } byte shift = eg_rate_shift[rate]; if (!(eg_cnt & ((1 << shift) -1))) { byte select = eg_rate_select[rate]; op.env_vol += eg_inc[select + ((eg_cnt >> shift) & 7)]; if ((unsigned(op.env_vol) > dl_tab[6]) && op.PRVB) { op.state = EG_REV; } else { if (op.env_vol >= MAX_ATT_INDEX) { op.env_vol = MAX_ATT_INDEX; op.active = false; } } } break; } case EG_REL: { // release phase byte rate = op.compute_rate(op.RR); if (rate < 4) { break; } byte shift = eg_rate_shift[rate]; if (!(eg_cnt & ((1 << shift) -1))) { byte select = eg_rate_select[rate]; op.env_vol += eg_inc[select + ((eg_cnt >> shift) & 7)]; if ((unsigned(op.env_vol) > dl_tab[6]) && op.PRVB) { op.state = EG_REV; } else { if (op.env_vol >= MAX_ATT_INDEX) { op.env_vol = MAX_ATT_INDEX; op.active = false; } } } break; } case EG_REV: { // pseudo reverb // TODO improve env_vol update byte rate = op.compute_rate(5); //if (rate < 4) { // break; //} byte shift = eg_rate_shift[rate]; if (!(eg_cnt & ((1 << shift) - 1))) { byte select = eg_rate_select[rate]; op.env_vol += eg_inc[select + ((eg_cnt >> shift) & 7)]; if (op.env_vol >= MAX_ATT_INDEX) { op.env_vol = MAX_ATT_INDEX; op.active = false; } } break; } case EG_DMP: { // damping // TODO improve env_vol update, damp is just fastest decay now byte rate = 56; byte shift = eg_rate_shift[rate]; if (!(eg_cnt & ((1 << shift) - 1))) { byte select = eg_rate_select[rate]; op.env_vol += eg_inc[select + ((eg_cnt >> shift) & 7)]; if (op.env_vol >= MAX_ATT_INDEX) { op.env_vol = MAX_ATT_INDEX; op.active = false; } } break; } case EG_OFF: // nothing break; default: UNREACHABLE; } } } short YMF278::Impl::getSample(YMF278Slot& op) { // TODO How does this behave when R#2 bit 0 = 1? // As-if read returns 0xff? (Like for CPU memory reads.) Or is // sound generation blocked at some higher level? short sample; switch (op.bits) { case 0: { // 8 bit sample = readMem(op.startaddr + op.pos) << 8; break; } case 1: { // 12 bit unsigned addr = op.startaddr + ((op.pos / 2) * 3); if (op.pos & 1) { sample = readMem(addr + 2) << 8 | ((readMem(addr + 1) << 4) & 0xF0); } else { sample = readMem(addr + 0) << 8 | (readMem(addr + 1) & 0xF0); } break; } case 2: { // 16 bit unsigned addr = op.startaddr + (op.pos * 2); sample = (readMem(addr + 0) << 8) | (readMem(addr + 1)); break; } default: // TODO unspecified sample = 0; } return sample; } bool YMF278::Impl::anyActive() { for (int i = 0; i < 24; ++i) { if (slots[i].active) { return true; } } return false; } void YMF278::Impl::generateChannels(int** bufs, unsigned num) { if (!anyActive()) { // TODO update internal state, even if muted // TODO also mute individual channels for (int i = 0; i < 24; ++i) { bufs[i] = nullptr; } return; } int vl = mix_level[pcm_l]; int vr = mix_level[pcm_r]; for (unsigned j = 0; j < num; ++j) { for (int i = 0; i < 24; ++i) { YMF278Slot& sl = slots[i]; if (!sl.active) { //bufs[i][2 * j + 0] += 0; //bufs[i][2 * j + 1] += 0; continue; } short sample = (sl.sample1 * (0x10000 - sl.stepptr) + sl.sample2 * sl.stepptr) >> 16; int vol = sl.TL + (sl.env_vol >> 2) + sl.compute_am(); int volLeft = vol + pan_left [int(sl.pan)] + vl; int volRight = vol + pan_right[int(sl.pan)] + vr; // TODO prob doesn't happen in real chip volLeft = std::max(0, volLeft); volRight = std::max(0, volRight); bufs[i][2 * j + 0] += (sample * volume[volLeft] ) >> 14; bufs[i][2 * j + 1] += (sample * volume[volRight]) >> 14; unsigned step = (sl.lfo_active && sl.vib) ? calcStep(sl.OCT, sl.FN, sl.compute_vib()) : sl.step; sl.stepptr += step; while (sl.stepptr >= 0x10000) { sl.stepptr -= 0x10000; sl.sample1 = sl.sample2; sl.pos++; if (sl.pos >= sl.endaddr) { sl.pos = sl.loopaddr; } sl.sample2 = getSample(sl); } } advance(); } } void YMF278::Impl::keyOnHelper(YMF278Slot& slot) { slot.active = true; slot.state = EG_ATT; slot.stepptr = 0; slot.pos = 0; slot.sample1 = getSample(slot); slot.pos = 1; slot.sample2 = getSample(slot); } void YMF278::Impl::writeReg(byte reg, byte data, EmuTime::param time) { updateStream(time); // TODO optimize only for regs that directly influence sound writeRegDirect(reg, data, time); } void YMF278::Impl::writeRegDirect(byte reg, byte data, EmuTime::param time) { // Handle slot registers specifically if (reg >= 0x08 && reg <= 0xF7) { int snum = (reg - 8) % 24; YMF278Slot& slot = slots[snum]; switch ((reg - 8) / 24) { case 0: { slot.wave = (slot.wave & 0x100) | data; int wavetblhdr = (regs[2] >> 2) & 0x7; int base = (slot.wave < 384 || !wavetblhdr) ? (slot.wave * 12) : (wavetblhdr * 0x80000 + ((slot.wave - 384) * 12)); byte buf[12]; for (int i = 0; i < 12; ++i) { // TODO What if R#2 bit 0 = 1? // See also getSample() buf[i] = readMem(base + i); } slot.bits = (buf[0] & 0xC0) >> 6; slot.startaddr = buf[2] | (buf[1] << 8) | ((buf[0] & 0x3F) << 16); slot.loopaddr = buf[4] + (buf[3] << 8); slot.endaddr = (((buf[6] + (buf[5] << 8)) ^ 0xFFFF) + 1); for (int i = 7; i < 12; ++i) { // Verified on real YMF278: // After tone loading, if you read these // registers, their value actually has changed. writeRegDirect(8 + snum + (i - 2) * 24, buf[i], time); } if ((regs[reg + 4] & 0x080)) { keyOnHelper(slot); } break; } case 1: { slot.wave = (slot.wave & 0xFF) | ((data & 0x1) << 8); slot.FN = (slot.FN & 0x380) | (data >> 1); slot.step = calcStep(slot.OCT, slot.FN); break; } case 2: { slot.FN = (slot.FN & 0x07F) | ((data & 0x07) << 7); slot.PRVB = ((data & 0x08) >> 3); slot.OCT = ((data & 0xF0) >> 4); slot.step = calcStep(slot.OCT, slot.FN); break; } case 3: slot.TL = data >> 1; slot.LD = data & 0x1; // TODO if (slot.LD) { // directly change volume } else { // interpolate volume } break; case 4: if (data & 0x10) { // output to DO1 pin: // this pin is not used in moonsound // we emulate this by muting the sound slot.pan = 8; // both left/right -inf dB } else { slot.pan = data & 0x0F; } if (data & 0x020) { // LFO reset slot.lfo_active = false; slot.lfo_cnt = 0; slot.lfo_max = lfo_period[int(slot.vib)]; slot.lfo_step = 0; } else { // LFO activate slot.lfo_active = true; } switch (data >> 6) { case 0: // tone off, no damp if (slot.active && (slot.state != EG_REV) ) { slot.state = EG_REL; } break; case 2: // tone on, no damp if (!(regs[reg] & 0x080)) { keyOnHelper(slot); } break; case 1: // tone off, damp case 3: // tone on, damp slot.state = EG_DMP; break; } break; case 5: slot.vib = data & 0x7; slot.set_lfo((data >> 3) & 0x7); break; case 6: slot.AR = data >> 4; slot.D1R = data & 0xF; break; case 7: slot.DL = dl_tab[data >> 4]; slot.D2R = data & 0xF; break; case 8: slot.RC = data >> 4; slot.RR = data & 0xF; break; case 9: slot.AM = data & 0x7; break; } } else { // All non-slot registers switch (reg) { case 0x00: // TEST case 0x01: break; case 0x02: // wave-table-header / memory-type / memory-access-mode // Simply store in regs[2] break; case 0x03: // Verified on real YMF278: // * Don't update the 'memadr' variable on writes to // reg 3 and 4. Only store the value in the 'regs' // array for later use. // * The upper 2 bits are not used to address the // external memories (so from a HW pov they don't // matter). But if you read back this register, the // upper 2 bits always read as '0' (even if you wrote // '1'). So we mask the bits here already. data &= 0x3F; break; case 0x04: // See reg 3. break; case 0x05: // Verified on real YMF278: (see above) // Only writes to reg 5 change the (full) 'memadr'. memadr = (regs[3] << 16) | (regs[4] << 8) | data; break; case 0x06: // memory data if (regs[2] & 1) { writeMem(memadr, data); ++memadr; // no need to mask (again) here } else { // Verified on real YMF278: // - writes are ignored // - memadr is NOT increased } break; case 0xF8: // TODO use these fm_l = data & 0x7; fm_r = (data >> 3) & 0x7; break; case 0xF9: pcm_l = data & 0x7; pcm_r = (data >> 3) & 0x7; break; } } regs[reg] = data; } byte YMF278::Impl::readReg(byte reg) { // no need to call updateStream(time) byte result = peekReg(reg); if (reg == 6) { // Memory Data Register if (regs[2] & 1) { // Verified on real YMF278: // memadr is only increased when 'regs[2] & 1' ++memadr; // no need to mask (again) here } } return result; } byte YMF278::Impl::peekReg(byte reg) const { byte result; switch (reg) { case 2: // 3 upper bits are device ID result = (regs[2] & 0x1F) | 0x20; break; case 6: // Memory Data Register if (regs[2] & 1) { result = readMem(memadr); } else { // Verified on real YMF278 result = 0xff; } break; default: result = regs[reg]; break; } return result; } YMF278::Impl::Impl(YMF278& self, const std::string& name, int ramSize, const DeviceConfig& config) : ResampledSoundDevice(config.getMotherBoard(), name, "MoonSound wave-part", 24, true) , motherBoard(config.getMotherBoard()) , debugRegisters(make_unique( self, motherBoard, getName())) , debugMemory(make_unique( self, motherBoard, getName())) , rom(make_unique(name + " ROM", "rom", config)) , ram(ramSize * 1024) // in kB { if (rom->getSize() != 0x200000) { // 2MB throw MSXException( "Wrong ROM for MoonSound (YMF278). The ROM (usually " "called yrw801.rom) should have a size of exactly 2MB."); } if ((ramSize != 0) && // - - (ramSize != 128) && // 128kB - (ramSize != 256) && // 128kB 128kB (ramSize != 512) && // 512kB - (ramSize != 640) && // 512kB 128kB (ramSize != 1024) && // 512kB 512kB (ramSize != 2048)) { // 512kB 512kB 512kB 512kB throw MSXException(StringOp::Builder() << "Wrong sampleram size for MoonSound (YMF278). " "Got " << ramSize << ", but must be one of " "0, 128, 256, 512, 640, 1024 or 2048."); } memadr = 0; // avoid UMR setInputRate(44100); reset(motherBoard.getCurrentTime()); registerSound(config); // Volume table, 1 = -0.375dB, 8 = -3dB, 256 = -96dB for (int i = 0; i < 256; ++i) { volume[i] = int(32768.0 * pow(2.0, (-0.375 / 6) * i)); } for (int i = 256; i < 256 * 4; ++i) { volume[i] = 0; } } YMF278::Impl::~Impl() { unregisterSound(); } void YMF278::Impl::clearRam() { memset(ram.data(), 0, ram.size()); } void YMF278::Impl::reset(EmuTime::param time) { updateStream(time); eg_cnt = 0; for (int i = 0; i < 24; ++i) { slots[i].reset(); } regs[2] = 0; // avoid UMR for (int i = 255; i >= 0; --i) { // reverse order to avoid UMR writeRegDirect(i, 0, time); } memadr = 0; fm_l = fm_r = pcm_l = pcm_r = 0; } // This routine translates an address from the (upper) MoonSound address space // to an address inside the (linearized) SRAM address space. // // The following info is based on measurements on a real MoonSound (v2.0) // PCB. This PCB can have several possible SRAM configurations: // 128kB: // 1 SRAM chip of 128kB, chip enable (/CE) of this SRAM chip is connected to // the 1Y0 output of a 74LS139 (2-to-4 decoder). The enable input of the // 74LS139 is connected to YMF278 pin /MCS6 and the 74LS139 1B:1A inputs are // connected to YMF278 pins MA18:MA17. So the SRAM is selected when /MC6 is // active and MA18:MA17 == 0:0. // 256kB: // 2 SRAM chips of 128kB. First one connected as above. Second one has /CE // connected to 74LS139 pin 1Y1. So SRAM2 is selected when /MSC6 is active // and MA18:MA17 == 0:1. // 512kB: // 1 SRAM chip of 512kB, /CE connected to /MCS6 // 640kB: // 1 SRAM chip of 512kB, /CE connected to /MCS6 // 1 SRAM chip of 128kB, /CE connected to /MCS7. // (This means SRAM2 is potentially mirrored over a 512kB region) // 1024kB: // 1 SRAM chip of 512kB, /CE connected to /MCS6 // 1 SRAM chip of 512kB, /CE connected to /MCS7 // 2048kB: // 1 SRAM chip of 512kB, /CE connected to /MCS6 // 1 SRAM chip of 512kB, /CE connected to /MCS7 // 1 SRAM chip of 512kB, /CE connected to /MCS8 // 1 SRAM chip of 512kB, /CE connected to /MCS9 // This configuration is not so easy to create on the v2.0 PCB. So it's // very rare. // // So the /MCS6 and /MCS7 (and /MCS8 and /MCS9 in case of 2048kB) signals are // used to select the different SRAM chips. The meaning of these signals // depends on the 'memory access mode'. This mode can be changed at run-time // via bit 1 in register 2. The following table indicates for which regions // these signals are active (normally MoonSound should be used with mode=0): // mode=0 mode=1 // /MCS6 0x200000-0x27FFFF 0x380000-0x39FFFF // /MCS7 0x280000-0x2FFFFF 0x3A0000-0x3BFFFF // /MCS8 0x300000-0x37FFFF 0x3C0000-0x3DFFFF // /MCS9 0x380000-0x3FFFFF 0x3E0000-0x3FFFFF // // (For completeness) MoonSound also has 2MB ROM (YRW801), /CE of this ROM is // connected to YMF278 /MCS0. In both mode=0 and mode=1 this signal is active // for the region 0x000000-0x1FFFFF. (But this routine does not handle ROM). unsigned YMF278::Impl::getRamAddress(unsigned addr) const { addr -= 0x200000; // RAM starts at 0x200000 if (unlikely(regs[2] & 2)) { // Normally MoonSound is used in 'memory access mode = 0'. But // in the rare case that mode=1 we adjust the address. if ((0x180000 <= addr) && (addr <= 0x1FFFFF)) { addr -= 0x180000; switch (addr & 0x060000) { case 0x000000: // [0x380000-0x39FFFF] // 1st 128kB of SRAM1 break; case 0x020000: // [0x3A0000-0x3BFFFF] if (ram.size() == 256*1024) { // 2nd 128kB SRAM chip } else { // 2nd block of 128kB in SRAM2 // In case of 512+128, we use mirroring addr += 0x080000; } break; case 0x040000: // [0x3C0000-0x3DFFFF] // 3rd 128kB block in SRAM3 addr += 0x100000; break; case 0x060000: // [0x3EFFFF-0x3FFFFF] // 4th 128kB block in SRAM4 addr += 0x180000; break; } } else { addr = unsigned(-1); // unmapped } } if (ram.size() == 640*1024) { // Verified on real MoonSound cartridge (v2.0): In case of // 640kB (1x512kB + 1x128kB), the 128kB SRAM chip is 4 times // visible. None of the other SRAM configurations show similar // mirroring (because the others are powers of two). if (addr > 0x080000) { addr &= ~0x060000; } } return addr; } byte YMF278::Impl::readMem(unsigned address) const { // Verified on real YMF278: address space wraps at 4MB. address &= 0x3FFFFF; if (address < 0x200000) { // ROM connected to /MCS0 return (*rom)[address]; } else { unsigned ramAddr = getRamAddress(address); if (ramAddr < ram.size()) { return ram[ramAddr]; } else { // unmapped region return 255; // TODO check } } } void YMF278::Impl::writeMem(unsigned address, byte value) { address &= 0x3FFFFF; if (address < 0x200000) { // can't write to ROM } else { unsigned ramAddr = getRamAddress(address); if (ramAddr < ram.size()) { ram[ramAddr] = value; } else { // can't write to unmapped memory } } } // version 1: initial version, some variables were saved as char // version 2: serialization framework was fixed to save/load chars as numbers // but for backwards compatibility we still load old savestates as // characters // version 3: 'step' is no longer stored (it is recalculated) template void YMF278Slot::serialize(Archive& ar, unsigned version) { // TODO restore more state from registers ar.serialize("startaddr", startaddr); ar.serialize("loopaddr", loopaddr); ar.serialize("endaddr", endaddr); ar.serialize("stepptr", stepptr); ar.serialize("pos", pos); ar.serialize("sample1", sample1); ar.serialize("sample2", sample2); ar.serialize("env_vol", env_vol); ar.serialize("lfo_cnt", lfo_cnt); ar.serialize("lfo_step", lfo_step); ar.serialize("lfo_max", lfo_max); ar.serialize("DL", DL); ar.serialize("wave", wave); ar.serialize("FN", FN); if (ar.versionAtLeast(version, 2)) { ar.serialize("OCT", OCT); ar.serialize("PRVB", PRVB); ar.serialize("LD", LD); ar.serialize("TL", TL); ar.serialize("pan", pan); ar.serialize("lfo", lfo); ar.serialize("vib", vib); ar.serialize("AM", AM); ar.serialize("AR", AR); ar.serialize("D1R", D1R); ar.serialize("D2R", D2R); ar.serialize("RC", RC); ar.serialize("RR", RR); } else { ar.serializeChar("OCT", OCT); ar.serializeChar("PRVB", PRVB); ar.serializeChar("LD", LD); ar.serializeChar("TL", TL); ar.serializeChar("pan", pan); ar.serializeChar("lfo", lfo); ar.serializeChar("vib", vib); ar.serializeChar("AM", AM); ar.serializeChar("AR", AR); ar.serializeChar("D1R", D1R); ar.serializeChar("D2R", D2R); ar.serializeChar("RC", RC); ar.serializeChar("RR", RR); } ar.serialize("bits", bits); ar.serialize("active", active); ar.serialize("state", state); ar.serialize("lfo_active", lfo_active); // Recalculate redundant state if (ar.isLoader()) { step = calcStep(OCT, FN); } // This old comment is NOT completely true: // Older version also had "env_vol_step" and "env_vol_lim" but those // members were nowhere used, so removed those in the current // version (it's ok to remove members from the savestate without // updating the version number). // When you remove member variables without increasing the version // number, new openMSX executables can still read old savestates. And // if you try to load a new savestate in an old openMSX version you do // get a (cryptic) error message. But if the version number is // increased the error message is much clearer. } // version 1: initial version // version 2: loadTime and busyTime moved to MSXMoonSound class // version 3: memadr cannot be restored from register values template void YMF278::Impl::serialize(Archive& ar, unsigned version) { ar.serialize("slots", slots); ar.serialize("eg_cnt", eg_cnt); ar.serialize_blob("ram", ram.data(), ram.size()); ar.serialize_blob("registers", regs, sizeof(regs)); if (ar.versionAtLeast(version, 3)) { // must come after 'regs' ar.serialize("memadr", memadr); } else { assert(ar.isLoader()); // Old formats didn't store 'memadr' so we also can't magically // restore the correct value. The best we can do is restore the // last set address. regs[3] &= 0x3F; // mask upper two bits memadr = (regs[3] << 16) | (regs[4] << 8) | regs[5]; } // TODO restore more state from registers static const byte rewriteRegs[] = { 0xf8, // fm_l, fm_r 0xf9, // pcm_l, pcm_r }; if (ar.isLoader()) { EmuTime::param time = motherBoard.getCurrentTime(); for (unsigned i = 0; i < sizeof(rewriteRegs); ++i) { byte reg = rewriteRegs[i]; writeRegDirect(reg, regs[reg], time); } } } // class DebugRegisters DebugRegisters::DebugRegisters(YMF278& ymf278_, MSXMotherBoard& motherBoard, const std::string& name) : SimpleDebuggable(motherBoard, name + " regs", "OPL4 registers", 0x100) , ymf278(ymf278_) { } byte DebugRegisters::read(unsigned address) { return ymf278.peekReg(address); } void DebugRegisters::write(unsigned address, byte value, EmuTime::param time) { ymf278.writeReg(address, value, time); } // class DebugMemory DebugMemory::DebugMemory(YMF278& ymf278_, MSXMotherBoard& motherBoard, const std::string& name) : SimpleDebuggable(motherBoard, name + " mem", "OPL4 memory (includes both ROM and RAM)", 0x400000) // 4MB , ymf278(ymf278_) { } byte DebugMemory::read(unsigned address) { return ymf278.readMem(address); } void DebugMemory::write(unsigned address, byte value) { ymf278.writeMem(address, value); } // class YMF278 YMF278::YMF278(const std::string& name, int ramSize, const DeviceConfig& config) : pimpl(make_unique(*this, name, ramSize, config)) { } YMF278::~YMF278() { } void YMF278::clearRam() { pimpl->clearRam(); } void YMF278::reset(EmuTime::param time) { pimpl->reset(time); } void YMF278::writeReg(byte reg, byte data, EmuTime::param time) { pimpl->writeReg(reg, data, time); } byte YMF278::readReg(byte reg) { return pimpl->readReg(reg); } byte YMF278::peekReg(byte reg) const { return pimpl->peekReg(reg); } byte YMF278::readMem(unsigned address) const { return pimpl->readMem(address); } void YMF278::writeMem(unsigned address, byte value) { pimpl->writeMem(address, value); } template void YMF278::serialize(Archive& ar, unsigned version) { pimpl->serialize(ar, version); } INSTANTIATE_SERIALIZE_METHODS(YMF278); } // namespace openmsx openmsx-0.10.0/src/sound/MSXFmPac.hh0000644000175000017500000000176612262345041017674 0ustar manuelmanuel00000000000000#ifndef MSXFMPAC_HH #define MSXFMPAC_HH #include "MSXMusic.hh" #include "serialize_meta.hh" #include namespace openmsx { class SRAM; class RomBlockDebuggable; class MSXFmPac : public MSXMusic { public: explicit MSXFmPac(const DeviceConfig& config); virtual ~MSXFmPac(); virtual void reset(EmuTime::param time); virtual void writeIO(word port, byte value, EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word address) const; virtual byte* getWriteCacheLine(word address) const; template void serialize(Archive& ar, unsigned version); private: void checkSramEnable(); const std::unique_ptr sram; const std::unique_ptr romBlockDebug; byte enable; byte bank; byte r1ffe, r1fff; bool sramEnabled; }; SERIALIZE_CLASS_VERSION(MSXFmPac, 2); // must be in-sync with MSXMusic } // namespace openmsx #endif openmsx-0.10.0/src/sound/AY8910.hh0000644000175000017500000001127312262345041017143 0ustar manuelmanuel00000000000000#ifndef AY8910_HH #define AY8910_HH #include "ResampledSoundDevice.hh" #include "openmsx.hh" #include namespace openmsx { class MSXMotherBoard; class AY8910Periphery; class DeviceConfig; class AY8910Debuggable; class FloatSetting; class TclCallback; /** This class implements the AY-3-8910 sound chip. * Only the AY-3-8910 is emulated, no surrounding hardware, * use the class AY8910Periphery to connect peripherals. */ class AY8910 : public ResampledSoundDevice { public: AY8910(const std::string& name, AY8910Periphery& periphery, const DeviceConfig& config, EmuTime::param time); virtual ~AY8910(); byte readRegister(unsigned reg, EmuTime::param time); byte peekRegister(unsigned reg, EmuTime::param time) const; void writeRegister(unsigned reg, byte value, EmuTime::param time); void reset(EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: class Generator { public: inline void reset(unsigned output); inline void setPeriod(int value); /** Gets the current output of this generator. */ inline unsigned getOutput() const; inline unsigned getNextEventTime() const; inline void advanceFast(unsigned duration); template void serialize(Archive& ar, unsigned version); protected: Generator(); /** Time between output steps. * For tones, this is half the period of the square wave. * For noise, this is the time before the random generator produces * its next output. */ int period; /** Time passed in this period. * Usually count will be smaller than period, but when the period * was recently changed this might not be the case. */ int count; /** Current state of the wave. * For tones, this is 0 or 1. */ unsigned output; }; class ToneGenerator : public Generator { public: ToneGenerator(); inline void setParent(AY8910& parent); /** Advance tone generator several steps in time. * @param duration Length of interval to simulate. */ inline void advance(int duration); inline void doNextEvent(bool doDetune); template void serialize(Archive& ar, unsigned version); private: int getDetune(); AY8910* parent; /** Time passed since start of vibrato cycle. */ unsigned vibratoCount; unsigned detuneCount; // disallow copy (can't use noncopyable utility for some reason) ToneGenerator(const ToneGenerator&); const ToneGenerator& operator=(const ToneGenerator&); }; class NoiseGenerator : public Generator { public: NoiseGenerator(); inline void reset(); /** Advance noise generator several steps in time. * @param duration Length of interval to simulate. */ inline void advance(int duration); inline void doNextEvent(); template void serialize(Archive& ar, unsigned version); private: int random; }; class Amplitude { public: explicit Amplitude(const DeviceConfig& config); const unsigned* getEnvVolTable() const; inline unsigned getVolume(unsigned chan) const; inline void setChannelVolume(unsigned chan, unsigned value); inline void setMasterVolume(int volume); inline bool followsEnvelope(unsigned chan) const; private: unsigned volTable[16]; unsigned envVolTable[32]; unsigned vol[3]; bool envChan[3]; const bool isAY8910; }; class Envelope { public: explicit inline Envelope(const unsigned* envVolTable); inline void reset(); inline void setPeriod(int value); inline void setShape(unsigned shape); inline bool isChanging() const; inline void advance(int duration); inline unsigned getVolume() const; inline unsigned getNextEventTime() const; inline void advanceFast(unsigned duration); inline void doNextEvent(); template void serialize(Archive& ar, unsigned version); private: inline void doSteps(int steps); const unsigned* envVolTable; int period; int count; int step; int attack; bool hold, alternate, holding; }; // SoundDevice virtual void generateChannels(int** bufs, unsigned num); // Observer virtual void update(const Setting& setting); void wrtReg(unsigned reg, byte value, EmuTime::param time); AY8910Periphery& periphery; const std::unique_ptr debuggable; const std::unique_ptr vibratoPercent; const std::unique_ptr vibratoFrequency; const std::unique_ptr detunePercent; const std::unique_ptr detuneFrequency; const std::unique_ptr directionsCallback; ToneGenerator tone[3]; NoiseGenerator noise; Amplitude amplitude; Envelope envelope; byte regs[16]; const bool isAY8910; bool doDetune; bool detuneInitialized; }; } // namespace openmsx #endif // AY8910_HH openmsx-0.10.0/src/sound/DACSound8U.hh0000644000175000017500000000055012262345041020121 0ustar manuelmanuel00000000000000// This class implements a 8 bit unsigned DAC #ifndef DACSOUND8U_HH #define DACSOUND8U_HH #include "DACSound16S.hh" namespace openmsx { class DACSound8U : public DACSound16S { public: DACSound8U(string_ref name, string_ref desc, const DeviceConfig& config); void writeDAC(byte value, EmuTime::param time); }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/YM2413Okazaki.cc0000644000175000017500000011731212262345041020450 0ustar manuelmanuel00000000000000/* * Based on: * emu2413.c -- YM2413 emulator written by Mitsutaka Okazaki 2001 * heavily rewritten to fit openMSX structure */ #include "YM2413Okazaki.hh" #include "serialize.hh" #include "inline.hh" #include "unreachable.hh" #include #include namespace openmsx { namespace YM2413Okazaki { // This defines the tables: // - signed char pmTable[8][8] // - int dB2LinTab[DBTABLEN * 2] // - unsigned AR_ADJUST_TABLE[1 << EG_BITS] // - byte tllTable[4][16 * 8] // - unsigned* waveform[2] // - int dphaseDRTable[16][16] // - byte lfo_am_table[LFO_AM_TAB_ELEMENTS] // - unsigned slTable[16] // - byte mlTable[16] #include "YM2413OkazakiTable.ii" // Extra (derived) constants static const EnvPhaseIndex EG_DP_MAX = EnvPhaseIndex(1 << 7); static const EnvPhaseIndex EG_DP_INF = EnvPhaseIndex(1 << 8); // as long as it's bigger // // Helper functions // static inline int EG2DB(int d) { return d * int(EG_STEP / DB_STEP); } static inline unsigned TL2EG(unsigned d) { assert(d < 64); // input is in range [0..63] return d * int(TL_STEP / EG_STEP); } static inline unsigned DB_POS(double x) { int result = int(x / DB_STEP); assert(0 <= result); assert(result < DB_MUTE); return result; } static inline unsigned DB_NEG(double x) { return DBTABLEN + DB_POS(x); } static inline bool BIT(unsigned s, unsigned b) { return (s >> b) & 1; } // // Patch // Patch::Patch() : AMPM(0), EG(false), AR(0), DR(0), RR(0) { setKR(0); setML(0); setKL(0); setTL(0); setWF(0); setFB(0); setSL(0); } void Patch::initModulator(const byte* data) { AMPM = (data[0] >> 6) & 3; EG = (data[0] >> 5) & 1; setKR ((data[0] >> 4) & 1); setML ((data[0] >> 0) & 15); setKL ((data[2] >> 6) & 3); setTL ((data[2] >> 0) & 63); setWF ((data[3] >> 3) & 1); setFB ((data[3] >> 0) & 7); AR = (data[4] >> 4) & 15; DR = (data[4] >> 0) & 15; setSL ((data[6] >> 4) & 15); RR = (data[6] >> 0) & 15; } void Patch::initCarrier(const byte* data) { AMPM = (data[1] >> 6) & 3; EG = (data[1] >> 5) & 1; setKR ((data[1] >> 4) & 1); setML ((data[1] >> 0) & 15); setKL ((data[3] >> 6) & 3); setTL (0); setWF ((data[3] >> 4) & 1); setFB (0); AR = (data[5] >> 4) & 15; DR = (data[5] >> 0) & 15; setSL ((data[7] >> 4) & 15); RR = (data[7] >> 0) & 15; } void Patch::setKR(byte value) { KR = value ? 8 : 10; } void Patch::setML(byte value) { ML = mlTable[value]; } void Patch::setKL(byte value) { KL = tllTable[value]; } void Patch::setTL(byte value) { assert(value < 64); assert(TL2EG(value) < 256); TL = TL2EG(value); } void Patch::setWF(byte value) { WF = waveform[value]; } void Patch::setFB(byte value) { FB = value ? 8 - value : 0; } void Patch::setSL(byte value) { SL = slTable[value]; } // // Slot // void Slot::reset() { cphase = 0; for (int i = 0; i < 8; ++i) dphase[i] = 0; output = 0; feedback = 0; setEnvelopeState(FINISH); dphaseDRTableRks = dphaseDRTable[0]; tll = 0; sustain = false; volume = TL2EG(0); slot_on_flag = 0; } void Slot::updatePG(unsigned freq) { // Pre-calculate all phase-increments. The 8 different values are for // the 8 steps of the PM stuff (for mod and car phase calculation). // When PM isn't used then dphase[0] is used (pmTable[.][0] == 0). // The original Okazaki core calculated the PM stuff in a different // way. This algorithm was copied from the Burczynski core because it // is much more suited for a (cheap) hardware calculation. unsigned fnum = freq & 511; unsigned block = freq / 512; for (int pm = 0; pm < 8; ++pm) { unsigned tmp = ((2 * fnum + pmTable[fnum >> 6][pm]) * patch.ML) << block; dphase[pm] = tmp >> (21 - DP_BITS); } } void Slot::updateTLL(unsigned freq, bool actAsCarrier) { tll = patch.KL[freq >> 5] + (actAsCarrier ? volume : patch.TL); } void Slot::updateRKS(unsigned freq) { unsigned rks = freq >> patch.KR; assert(rks < 16); dphaseDRTableRks = dphaseDRTable[rks]; } void Slot::updateEG() { switch (state) { case ATTACK: // Original code had separate table for AR, the values in // this table were 12 times bigger than the values in the // dphaseDRTableRks table, expect for the value for AR=15. // But when AR=15, the value doesn't matter. // // This factor 12 can also be seen in the attack/decay rates // table in the ym2413 application manual (table III-7, page // 13). For other chips like OPL1, OPL3 this ratio seems to be // different. eg_dphase = EnvPhaseIndex::create( dphaseDRTableRks[patch.AR] * 12); break; case DECAY: eg_dphase = EnvPhaseIndex::create(dphaseDRTableRks[patch.DR]); break; case SUSTAIN: eg_dphase = EnvPhaseIndex::create(dphaseDRTableRks[patch.RR]); break; case RELEASE: { unsigned idx = sustain ? 5 : (patch.EG ? patch.RR : 7); eg_dphase = EnvPhaseIndex::create(dphaseDRTableRks[idx]); break; } case SETTLE: // Value based on ym2413 application manual: // - p10: (envelope graph) // DP: 10ms // - p13: (table III-7 attack and decay rates) // Rate 12-0 -> 10.22ms // Rate 12-1 -> 8.21ms // Rate 12-2 -> 6.84ms // Rate 12-3 -> 5.87ms // The datasheet doesn't say anything about key-scaling for // this state (in fact it doesn't say much at all about this // state). Experiments showed that the with key-scaling the // output matches closer the real HW. Also all other states use // key-scaling. eg_dphase = EnvPhaseIndex::create(dphaseDRTableRks[12]); break; case SUSHOLD: case FINISH: eg_dphase = EnvPhaseIndex(0); break; } } void Slot::updateAll(unsigned freq, bool actAsCarrier) { updatePG(freq); updateTLL(freq, actAsCarrier); updateRKS(freq); updateEG(); // EG should be updated last } void Slot::setEnvelopeState(EnvelopeState state_) { state = state_; switch (state) { case ATTACK: eg_phase_max = (patch.AR == 15) ? EnvPhaseIndex(0) : EG_DP_MAX; break; case DECAY: eg_phase_max = EnvPhaseIndex::create(patch.SL); break; case SUSHOLD: eg_phase_max = EG_DP_INF; break; case SETTLE: case SUSTAIN: case RELEASE: eg_phase_max = EG_DP_MAX; break; case FINISH: eg_phase = EG_DP_MAX; eg_phase_max = EG_DP_INF; break; } updateEG(); } bool Slot::isActive() const { return state != FINISH; } // Slot key on void Slot::slotOn() { setEnvelopeState(ATTACK); eg_phase = EnvPhaseIndex(0); cphase = 0; } // Slot key on, without resetting the phase void Slot::slotOn2() { setEnvelopeState(ATTACK); eg_phase = EnvPhaseIndex(0); } // Slot key off void Slot::slotOff() { if (state == FINISH) return; // already in off state if (state == ATTACK) { eg_phase = EnvPhaseIndex(AR_ADJUST_TABLE[eg_phase.toInt()]); } setEnvelopeState(RELEASE); } // Change a rhythm voice void Slot::setPatch(Patch& patch) { this->patch = patch; // copy data setEnvelopeState(state); // recalc eg_phase_max } // Set new volume based on 4-bit register value (0-15). void Slot::setVolume(unsigned value) { // '<< 2' to transform 4 bits to the same range as the 6 bits TL field volume = TL2EG(value << 2); } // // Channel // Channel::Channel() { car.sibling = &mod; // car needs a pointer to its sibling mod.sibling = nullptr; // mod doesn't need this pointer } void Channel::reset(YM2413& ym2413) { mod.reset(); car.reset(); setPatch(0, ym2413); } // Change a voice void Channel::setPatch(unsigned num, YM2413& ym2413) { mod.setPatch(ym2413.getPatch(num, false)); car.setPatch(ym2413.getPatch(num, true)); } // Set sustain parameter void Channel::setSustain(bool sustain, bool modActAsCarrier) { car.sustain = sustain; if (modActAsCarrier) { mod.sustain = sustain; } } // Channel key on void Channel::keyOn() { // TODO Should we also test mod.slot_on_flag? // Should we set mod.slot_on_flag? // Can make a difference for channel 7/8 in ryhthm mode. if (!car.slot_on_flag) { car.setEnvelopeState(SETTLE); // this will shortly set both car and mod to ATTACK state } car.slot_on_flag |= 1; mod.slot_on_flag |= 1; } // Channel key off void Channel::keyOff() { // Note: no mod.slotOff() in original code!!! if (car.slot_on_flag) { car.slot_on_flag &= ~1; mod.slot_on_flag &= ~1; if (!car.slot_on_flag) { car.slotOff(); } } } // // YM2413 // static byte inst_data[16 + 3][8] = { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }, // user instrument { 0x61,0x61,0x1e,0x17,0xf0,0x7f,0x00,0x17 }, // violin { 0x13,0x41,0x16,0x0e,0xfd,0xf4,0x23,0x23 }, // guitar { 0x03,0x01,0x9a,0x04,0xf3,0xf3,0x13,0xf3 }, // piano { 0x11,0x61,0x0e,0x07,0xfa,0x64,0x70,0x17 }, // flute { 0x22,0x21,0x1e,0x06,0xf0,0x76,0x00,0x28 }, // clarinet { 0x21,0x22,0x16,0x05,0xf0,0x71,0x00,0x18 }, // oboe { 0x21,0x61,0x1d,0x07,0x82,0x80,0x17,0x17 }, // trumpet { 0x23,0x21,0x2d,0x16,0x90,0x90,0x00,0x07 }, // organ { 0x21,0x21,0x1b,0x06,0x64,0x65,0x10,0x17 }, // horn { 0x21,0x21,0x0b,0x1a,0x85,0xa0,0x70,0x07 }, // synthesizer { 0x23,0x01,0x83,0x10,0xff,0xb4,0x10,0xf4 }, // harpsichord { 0x97,0xc1,0x20,0x07,0xff,0xf4,0x22,0x22 }, // vibraphone { 0x61,0x00,0x0c,0x05,0xc2,0xf6,0x40,0x44 }, // synthesizer bass { 0x01,0x01,0x56,0x03,0x94,0xc2,0x03,0x12 }, // acoustic bass { 0x21,0x01,0x89,0x03,0xf1,0xe4,0xf0,0x23 }, // electric guitar { 0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8 }, { 0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7 }, { 0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55 } }; YM2413::YM2413() { memset(reg, 0, sizeof(reg)); // avoid UMR for (unsigned i = 0; i < 16 + 3; ++i) { patches[i][0].initModulator(inst_data[i]); patches[i][1].initCarrier (inst_data[i]); } reset(); } // Reset whole of OPLL except patch datas void YM2413::reset() { pm_phase = 0; am_phase = 0; noise_seed = 0xFFFF; idleSamples = 0; for (unsigned i = 0; i < 9; ++i) { channels[i].reset(*this); } for (unsigned i = 0; i < 0x40; ++i) { writeReg(i, 0); } } // Drum key on void YM2413::keyOn_BD() { Channel& ch6 = channels[6]; if (!ch6.car.slot_on_flag) { ch6.car.setEnvelopeState(SETTLE); // this will shortly set both car and mod to ATTACK state } ch6.car.slot_on_flag |= 2; ch6.mod.slot_on_flag |= 2; } void YM2413::keyOn_HH() { // TODO do these also use the SETTLE stuff? Channel& ch7 = channels[7]; if (!ch7.mod.slot_on_flag) { ch7.mod.slotOn2(); } ch7.mod.slot_on_flag |= 2; } void YM2413::keyOn_SD() { Channel& ch7 = channels[7]; if (!ch7.car.slot_on_flag) { ch7.car.slotOn(); } ch7.car.slot_on_flag |= 2; } void YM2413::keyOn_TOM() { Channel& ch8 = channels[8]; if (!ch8.mod.slot_on_flag) { ch8.mod.slotOn(); } ch8.mod.slot_on_flag |= 2; } void YM2413::keyOn_CYM() { Channel& ch8 = channels[8]; if (!ch8.car.slot_on_flag) { ch8.car.slotOn2(); } ch8.car.slot_on_flag |= 2; } // Drum key off void YM2413::keyOff_BD() { Channel& ch6 = channels[6]; if (ch6.car.slot_on_flag) { ch6.car.slot_on_flag &= ~2; ch6.mod.slot_on_flag &= ~2; if (!ch6.car.slot_on_flag) { ch6.car.slotOff(); } } } void YM2413::keyOff_HH() { Channel& ch7 = channels[7]; if (ch7.mod.slot_on_flag) { ch7.mod.slot_on_flag &= ~2; if (!ch7.mod.slot_on_flag) { ch7.mod.slotOff(); } } } void YM2413::keyOff_SD() { Channel& ch7 = channels[7]; if (ch7.car.slot_on_flag) { ch7.car.slot_on_flag &= ~2; if (!ch7.car.slot_on_flag) { ch7.car.slotOff(); } } } void YM2413::keyOff_TOM() { Channel& ch8 = channels[8]; if (ch8.mod.slot_on_flag) { ch8.mod.slot_on_flag &= ~2; if (!ch8.mod.slot_on_flag) { ch8.mod.slotOff(); } } } void YM2413::keyOff_CYM() { Channel& ch8 = channels[8]; if (ch8.car.slot_on_flag) { ch8.car.slot_on_flag &= ~2; if (!ch8.car.slot_on_flag) { ch8.car.slotOff(); } } } void YM2413::setRhythmFlags(byte old) { Channel& ch6 = channels[6]; Channel& ch7 = channels[7]; Channel& ch8 = channels[8]; // flags = X | X | mode | BD | SD | TOM | TC | HH byte flags = reg[0x0E]; if ((flags ^ old) & 0x20) { if (flags & 0x20) { // OFF -> ON ch6.setPatch(16, *this); ch7.setPatch(17, *this); ch7.mod.setVolume(reg[0x37] >> 4); ch8.setPatch(18, *this); ch8.mod.setVolume(reg[0x38] >> 4); } else { // ON -> OFF ch6.setPatch(reg[0x36] >> 4, *this); keyOff_BD(); ch7.setPatch(reg[0x37] >> 4, *this); keyOff_SD(); keyOff_HH(); ch8.setPatch(reg[0x38] >> 4, *this); keyOff_TOM(); keyOff_CYM(); } } if (flags & 0x20) { if (flags & 0x10) keyOn_BD(); else keyOff_BD(); if (flags & 0x08) keyOn_SD(); else keyOff_SD(); if (flags & 0x04) keyOn_TOM(); else keyOff_TOM(); if (flags & 0x02) keyOn_CYM(); else keyOff_CYM(); if (flags & 0x01) keyOn_HH(); else keyOff_HH(); } unsigned freq6 = getFreq(6); ch6.mod.updateAll(freq6, false); ch6.car.updateAll(freq6, true); unsigned freq7 = getFreq(7); ch7.mod.updateAll(freq7, isRhythm()); ch7.car.updateAll(freq7, true); unsigned freq8 = getFreq(8); ch8.mod.updateAll(freq8, isRhythm()); ch8.car.updateAll(freq8, true); } void YM2413::update_key_status() { for (unsigned i = 0; i < 9; ++i) { int slot_on = (reg[0x20 + i] & 0x10) ? 1 : 0; Channel& ch = channels[i]; ch.mod.slot_on_flag = slot_on; ch.car.slot_on_flag = slot_on; } if (isRhythm()) { Channel& ch6 = channels[6]; ch6.mod.slot_on_flag |= (reg[0x0e] & 0x10) ? 2 : 0; // BD1 ch6.car.slot_on_flag |= (reg[0x0e] & 0x10) ? 2 : 0; // BD2 Channel& ch7 = channels[7]; ch7.mod.slot_on_flag |= (reg[0x0e] & 0x01) ? 2 : 0; // HH ch7.car.slot_on_flag |= (reg[0x0e] & 0x08) ? 2 : 0; // SD Channel& ch8 = channels[8]; ch8.mod.slot_on_flag |= (reg[0x0e] & 0x04) ? 2 : 0; // TOM ch8.car.slot_on_flag |= (reg[0x0e] & 0x02) ? 2 : 0; // CYM } } // Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI) static inline int wave2_8pi(int e) { int shift = SLOT_AMP_BITS - PG_BITS - 2; if (shift > 0) { return e >> shift; } else { return e << -shift; } } // PG ALWAYS_INLINE unsigned Slot::calc_phase(unsigned lfo_pm) { cphase += dphase[lfo_pm]; return cphase >> DP_BASE_BITS; } // EG void Slot::calc_envelope_outline(unsigned& out) { switch (state) { case ATTACK: out = 0; eg_phase = EnvPhaseIndex(0); setEnvelopeState(DECAY); break; case DECAY: eg_phase = eg_phase_max; setEnvelopeState(patch.EG ? SUSHOLD : SUSTAIN); break; case SUSTAIN: case RELEASE: setEnvelopeState(FINISH); break; case SETTLE: // Comment copied from Burczynski code (didn't verify myself): // When CARRIER envelope gets down to zero level, phases in // BOTH operators are reset (at the same time?). slotOn(); sibling->slotOn(); break; case SUSHOLD: case FINISH: default: UNREACHABLE; break; } } template ALWAYS_INLINE unsigned Slot::calc_envelope(int lfo_am, unsigned fixed_env) { assert(!FIXED_ENV || (state == SUSHOLD) || (state == FINISH)); if (FIXED_ENV) { unsigned out = fixed_env; if (HAS_AM) { out += lfo_am; // [0, 512) out |= 3; } else { // out |= 3 is already done in calc_fixed_env() } return out; } else { unsigned out = eg_phase.toInt(); // in range [0, 128) if (state == ATTACK) { out = AR_ADJUST_TABLE[out]; // [0, 128) } eg_phase += eg_dphase; if (eg_phase >= eg_phase_max) { calc_envelope_outline(out); } out = EG2DB(out + tll); // [0, 480) if (HAS_AM) { out += lfo_am; // [0, 512) } return out | 3; } } template unsigned Slot::calc_fixed_env() const { assert((state == SUSHOLD) || (state == FINISH)); assert(eg_dphase == EnvPhaseIndex(0)); unsigned out = eg_phase.toInt(); // in range [0, 128) out = EG2DB(out + tll); // [0, 480) if (!HAS_AM) { out |= 3; } return out; } // CARRIER template ALWAYS_INLINE int Slot::calc_slot_car(unsigned lfo_pm, int lfo_am, int fm, unsigned fixed_env) { int phase = calc_phase(lfo_pm) + wave2_8pi(fm); unsigned egout = calc_envelope(lfo_am, fixed_env); int newOutput = dB2LinTab[patch.WF[phase & PG_MASK] + egout]; output = (output + newOutput) >> 1; return output; } // MODULATOR template ALWAYS_INLINE int Slot::calc_slot_mod(unsigned lfo_pm, int lfo_am, unsigned fixed_env) { assert((patch.FB != 0) == HAS_FB); unsigned phase = calc_phase(lfo_pm); unsigned egout = calc_envelope(lfo_am, fixed_env); if (HAS_FB) { phase += wave2_8pi(feedback) >> patch.FB; } int newOutput = dB2LinTab[patch.WF[phase & PG_MASK] + egout]; feedback = (output + newOutput) >> 1; output = newOutput; return feedback; } // TOM (ch8 mod) ALWAYS_INLINE int Slot::calc_slot_tom() { unsigned phase = calc_phase(0); unsigned egout = calc_envelope(0, 0); return dB2LinTab[patch.WF[phase & PG_MASK] + egout]; } // SNARE (ch7 car) ALWAYS_INLINE int Slot::calc_slot_snare(bool noise) { unsigned phase = calc_phase(0); unsigned egout = calc_envelope(0, 0); return BIT(phase, 7) ? dB2LinTab[(noise ? DB_POS(0.0) : DB_POS(15.0)) + egout] : dB2LinTab[(noise ? DB_NEG(0.0) : DB_NEG(15.0)) + egout]; } // TOP-CYM (ch8 car) ALWAYS_INLINE int Slot::calc_slot_cym(unsigned phase7, unsigned phase8) { unsigned egout = calc_envelope(0, 0); unsigned dbout = (((BIT(phase7, PG_BITS - 8) ^ BIT(phase7, PG_BITS - 1)) | BIT(phase7, PG_BITS - 7)) ^ ( BIT(phase8, PG_BITS - 7) & !BIT(phase8, PG_BITS - 5))) ? DB_NEG(3.0) : DB_POS(3.0); return dB2LinTab[dbout + egout]; } // HI-HAT (ch7 mod) ALWAYS_INLINE int Slot::calc_slot_hat(unsigned phase7, unsigned phase8, bool noise) { unsigned egout = calc_envelope(0, 0); unsigned dbout = (((BIT(phase7, PG_BITS - 8) ^ BIT(phase7, PG_BITS - 1)) | BIT(phase7, PG_BITS - 7)) ^ ( BIT(phase8, PG_BITS - 7) & !BIT(phase8, PG_BITS - 5))) ? (noise ? DB_NEG(12.0) : DB_NEG(24.0)) : (noise ? DB_POS(12.0) : DB_POS(24.0)); return dB2LinTab[dbout + egout]; } int YM2413::getAmplificationFactor() const { return 1 << (15 - DB2LIN_AMP_BITS); } bool YM2413::isRhythm() const { return (reg[0x0E] & 0x20) != 0; } unsigned YM2413::getFreq(unsigned channel) const { // combined fnum (=9bit) and block (=3bit) assert(channel < 9); return reg[0x10 + channel] | ((reg[0x20 + channel] & 0x0F) << 8); } Patch& YM2413::getPatch(unsigned instrument, bool carrier) { return patches[instrument][carrier]; } template ALWAYS_INLINE void YM2413::calcChannel(Channel& ch, int* buf, unsigned num) { // VC++ requires explicit conversion to bool. Compiler bug?? const bool HAS_CAR_PM = (FLAGS & 1) != 0; const bool HAS_CAR_AM = (FLAGS & 2) != 0; const bool HAS_MOD_PM = (FLAGS & 4) != 0; const bool HAS_MOD_AM = (FLAGS & 8) != 0; const bool HAS_MOD_FB = (FLAGS & 16) != 0; const bool HAS_CAR_FIXED_ENV = (FLAGS & 32) != 0; const bool HAS_MOD_FIXED_ENV = (FLAGS & 64) != 0; assert(((ch.car.patch.AMPM & 1) != 0) == HAS_CAR_PM); assert(((ch.car.patch.AMPM & 2) != 0) == HAS_CAR_AM); assert(((ch.mod.patch.AMPM & 1) != 0) == HAS_MOD_PM); assert(((ch.mod.patch.AMPM & 2) != 0) == HAS_MOD_AM); unsigned tmp_pm_phase = pm_phase; unsigned tmp_am_phase = am_phase; unsigned car_fixed_env = 0; // dummy unsigned mod_fixed_env = 0; // dummy if (HAS_CAR_FIXED_ENV) { car_fixed_env = ch.car.calc_fixed_env(); } if (HAS_MOD_FIXED_ENV) { mod_fixed_env = ch.mod.calc_fixed_env(); } unsigned sample = 0; do { unsigned lfo_pm = 0; if (HAS_CAR_PM || HAS_MOD_PM) { // Copied from Burczynski: // There are only 8 different steps for PM, and each // step lasts for 1024 samples. This results in a PM // freq of 6.1Hz (but datasheet says it's 6.4Hz). ++tmp_pm_phase; lfo_pm = (tmp_pm_phase >> 10) & 7; } int lfo_am = 0; // avoid warning if (HAS_CAR_AM || HAS_MOD_AM) { ++tmp_am_phase; if (tmp_am_phase == (LFO_AM_TAB_ELEMENTS * 64)) { tmp_am_phase = 0; } lfo_am = lfo_am_table[tmp_am_phase / 64]; } int fm = ch.mod.calc_slot_mod( lfo_pm, lfo_am, mod_fixed_env); buf[sample] += ch.car.calc_slot_car( lfo_pm, lfo_am, fm, car_fixed_env); ++sample; } while (sample < num); } void YM2413::generateChannels(int* bufs[9 + 5], unsigned num) { assert(num != 0); // TODO make channelActiveBits a member and // keep it up-to-date all the time // bits 0-8 -> ch[0-8].car // bits 9-17 -> ch[0-8].mod (only ch7 and ch8 are used) unsigned channelActiveBits = 0; unsigned m = isRhythm() ? 6 : 9; for (unsigned ch = 0; ch < m; ++ch) { if (channels[ch].car.isActive()) { channelActiveBits |= 1 << ch; } else { bufs[ch] = nullptr; } } if (isRhythm()) { bufs[6] = nullptr; bufs[7] = nullptr; bufs[8] = nullptr; for (unsigned ch = 6; ch < 9; ++ch) { if (channels[ch].car.isActive()) { channelActiveBits |= 1 << ch; } else { bufs[ch + 3] = nullptr; } } if (channels[7].mod.isActive()) { channelActiveBits |= 1 << (7 + 9); } else { bufs[12] = nullptr; } if (channels[8].mod.isActive()) { channelActiveBits |= 1 << (8 + 9); } else { bufs[13] = nullptr; } } else { bufs[ 9] = nullptr; bufs[10] = nullptr; bufs[11] = nullptr; bufs[12] = nullptr; bufs[13] = nullptr; } if (channelActiveBits) { idleSamples = 0; } else { if (idleSamples > (CLOCK_FREQ / (72 * 5))) { // Optimization: // idle for over 1/5s = 200ms // we don't care that noise / AM / PM isn't exactly // in sync with the real HW when music resumes // Alternative: // implement an efficient advance(n) method return; } idleSamples += num; } for (unsigned i = 0; i < m; ++i) { if (channelActiveBits & (1 << i)) { // below we choose between 32 specialized versions of // calcChannel() this allows to move a lot of // conditional code out of the inner-loop Channel& ch = channels[i]; bool carFixedEnv = (ch.car.state == SUSHOLD) || (ch.car.state == FINISH); bool modFixedEnv = (ch.mod.state == SUSHOLD) || (ch.mod.state == FINISH); if (ch.car.state == SETTLE) { modFixedEnv = false; } unsigned flags = ( ch.car.patch.AMPM << 0) | ( ch.mod.patch.AMPM << 2) | ((ch.mod.patch.FB != 0) << 4) | ( carFixedEnv << 5) | ( modFixedEnv << 6); switch (flags) { case 0: calcChannel< 0>(ch, bufs[i], num); break; case 1: calcChannel< 1>(ch, bufs[i], num); break; case 2: calcChannel< 2>(ch, bufs[i], num); break; case 3: calcChannel< 3>(ch, bufs[i], num); break; case 4: calcChannel< 4>(ch, bufs[i], num); break; case 5: calcChannel< 5>(ch, bufs[i], num); break; case 6: calcChannel< 6>(ch, bufs[i], num); break; case 7: calcChannel< 7>(ch, bufs[i], num); break; case 8: calcChannel< 8>(ch, bufs[i], num); break; case 9: calcChannel< 9>(ch, bufs[i], num); break; case 10: calcChannel< 10>(ch, bufs[i], num); break; case 11: calcChannel< 11>(ch, bufs[i], num); break; case 12: calcChannel< 12>(ch, bufs[i], num); break; case 13: calcChannel< 13>(ch, bufs[i], num); break; case 14: calcChannel< 14>(ch, bufs[i], num); break; case 15: calcChannel< 15>(ch, bufs[i], num); break; case 16: calcChannel< 16>(ch, bufs[i], num); break; case 17: calcChannel< 17>(ch, bufs[i], num); break; case 18: calcChannel< 18>(ch, bufs[i], num); break; case 19: calcChannel< 19>(ch, bufs[i], num); break; case 20: calcChannel< 20>(ch, bufs[i], num); break; case 21: calcChannel< 21>(ch, bufs[i], num); break; case 22: calcChannel< 22>(ch, bufs[i], num); break; case 23: calcChannel< 23>(ch, bufs[i], num); break; case 24: calcChannel< 24>(ch, bufs[i], num); break; case 25: calcChannel< 25>(ch, bufs[i], num); break; case 26: calcChannel< 26>(ch, bufs[i], num); break; case 27: calcChannel< 27>(ch, bufs[i], num); break; case 28: calcChannel< 28>(ch, bufs[i], num); break; case 29: calcChannel< 29>(ch, bufs[i], num); break; case 30: calcChannel< 30>(ch, bufs[i], num); break; case 31: calcChannel< 31>(ch, bufs[i], num); break; case 32: calcChannel< 32>(ch, bufs[i], num); break; case 33: calcChannel< 33>(ch, bufs[i], num); break; case 34: calcChannel< 34>(ch, bufs[i], num); break; case 35: calcChannel< 35>(ch, bufs[i], num); break; case 36: calcChannel< 36>(ch, bufs[i], num); break; case 37: calcChannel< 37>(ch, bufs[i], num); break; case 38: calcChannel< 38>(ch, bufs[i], num); break; case 39: calcChannel< 39>(ch, bufs[i], num); break; case 40: calcChannel< 40>(ch, bufs[i], num); break; case 41: calcChannel< 41>(ch, bufs[i], num); break; case 42: calcChannel< 42>(ch, bufs[i], num); break; case 43: calcChannel< 43>(ch, bufs[i], num); break; case 44: calcChannel< 44>(ch, bufs[i], num); break; case 45: calcChannel< 45>(ch, bufs[i], num); break; case 46: calcChannel< 46>(ch, bufs[i], num); break; case 47: calcChannel< 47>(ch, bufs[i], num); break; case 48: calcChannel< 48>(ch, bufs[i], num); break; case 49: calcChannel< 49>(ch, bufs[i], num); break; case 50: calcChannel< 50>(ch, bufs[i], num); break; case 51: calcChannel< 51>(ch, bufs[i], num); break; case 52: calcChannel< 52>(ch, bufs[i], num); break; case 53: calcChannel< 53>(ch, bufs[i], num); break; case 54: calcChannel< 54>(ch, bufs[i], num); break; case 55: calcChannel< 55>(ch, bufs[i], num); break; case 56: calcChannel< 56>(ch, bufs[i], num); break; case 57: calcChannel< 57>(ch, bufs[i], num); break; case 58: calcChannel< 58>(ch, bufs[i], num); break; case 59: calcChannel< 59>(ch, bufs[i], num); break; case 60: calcChannel< 60>(ch, bufs[i], num); break; case 61: calcChannel< 61>(ch, bufs[i], num); break; case 62: calcChannel< 62>(ch, bufs[i], num); break; case 63: calcChannel< 63>(ch, bufs[i], num); break; case 64: calcChannel< 64>(ch, bufs[i], num); break; case 65: calcChannel< 65>(ch, bufs[i], num); break; case 66: calcChannel< 66>(ch, bufs[i], num); break; case 67: calcChannel< 67>(ch, bufs[i], num); break; case 68: calcChannel< 68>(ch, bufs[i], num); break; case 69: calcChannel< 69>(ch, bufs[i], num); break; case 70: calcChannel< 70>(ch, bufs[i], num); break; case 71: calcChannel< 71>(ch, bufs[i], num); break; case 72: calcChannel< 72>(ch, bufs[i], num); break; case 73: calcChannel< 73>(ch, bufs[i], num); break; case 74: calcChannel< 74>(ch, bufs[i], num); break; case 75: calcChannel< 75>(ch, bufs[i], num); break; case 76: calcChannel< 76>(ch, bufs[i], num); break; case 77: calcChannel< 77>(ch, bufs[i], num); break; case 78: calcChannel< 78>(ch, bufs[i], num); break; case 79: calcChannel< 79>(ch, bufs[i], num); break; case 80: calcChannel< 80>(ch, bufs[i], num); break; case 81: calcChannel< 81>(ch, bufs[i], num); break; case 82: calcChannel< 82>(ch, bufs[i], num); break; case 83: calcChannel< 83>(ch, bufs[i], num); break; case 84: calcChannel< 84>(ch, bufs[i], num); break; case 85: calcChannel< 85>(ch, bufs[i], num); break; case 86: calcChannel< 86>(ch, bufs[i], num); break; case 87: calcChannel< 87>(ch, bufs[i], num); break; case 88: calcChannel< 88>(ch, bufs[i], num); break; case 89: calcChannel< 89>(ch, bufs[i], num); break; case 90: calcChannel< 90>(ch, bufs[i], num); break; case 91: calcChannel< 91>(ch, bufs[i], num); break; case 92: calcChannel< 92>(ch, bufs[i], num); break; case 93: calcChannel< 93>(ch, bufs[i], num); break; case 94: calcChannel< 94>(ch, bufs[i], num); break; case 95: calcChannel< 95>(ch, bufs[i], num); break; case 96: calcChannel< 96>(ch, bufs[i], num); break; case 97: calcChannel< 97>(ch, bufs[i], num); break; case 98: calcChannel< 98>(ch, bufs[i], num); break; case 99: calcChannel< 99>(ch, bufs[i], num); break; case 100: calcChannel<100>(ch, bufs[i], num); break; case 101: calcChannel<101>(ch, bufs[i], num); break; case 102: calcChannel<102>(ch, bufs[i], num); break; case 103: calcChannel<103>(ch, bufs[i], num); break; case 104: calcChannel<104>(ch, bufs[i], num); break; case 105: calcChannel<105>(ch, bufs[i], num); break; case 106: calcChannel<106>(ch, bufs[i], num); break; case 107: calcChannel<107>(ch, bufs[i], num); break; case 108: calcChannel<108>(ch, bufs[i], num); break; case 109: calcChannel<109>(ch, bufs[i], num); break; case 110: calcChannel<110>(ch, bufs[i], num); break; case 111: calcChannel<111>(ch, bufs[i], num); break; case 112: calcChannel<112>(ch, bufs[i], num); break; case 113: calcChannel<113>(ch, bufs[i], num); break; case 114: calcChannel<114>(ch, bufs[i], num); break; case 115: calcChannel<115>(ch, bufs[i], num); break; case 116: calcChannel<116>(ch, bufs[i], num); break; case 117: calcChannel<117>(ch, bufs[i], num); break; case 118: calcChannel<118>(ch, bufs[i], num); break; case 119: calcChannel<119>(ch, bufs[i], num); break; case 120: calcChannel<120>(ch, bufs[i], num); break; case 121: calcChannel<121>(ch, bufs[i], num); break; case 122: calcChannel<122>(ch, bufs[i], num); break; case 123: calcChannel<123>(ch, bufs[i], num); break; case 124: calcChannel<124>(ch, bufs[i], num); break; case 125: calcChannel<125>(ch, bufs[i], num); break; case 126: calcChannel<126>(ch, bufs[i], num); break; case 127: calcChannel<127>(ch, bufs[i], num); break; default: UNREACHABLE; } } } // update AM, PM unit pm_phase += num; am_phase = (am_phase + num) % (LFO_AM_TAB_ELEMENTS * 64); if (isRhythm()) { if (channelActiveBits & (1 << 6)) { Channel& ch6 = channels[6]; for (unsigned sample = 0; sample < num; ++sample) { bufs[ 9][sample] += 2 * ch6.car.calc_slot_car( 0, 0, ch6.mod.calc_slot_mod< false, false, false>(0, 0, 0), 0); } } Channel& ch7 = channels[7]; Channel& ch8 = channels[8]; unsigned old_noise = noise_seed; if (channelActiveBits & (1 << 7)) { for (unsigned sample = 0; sample < num; ++sample) { noise_seed >>= 1; bool noise_bit = noise_seed & 1; if (noise_bit) noise_seed ^= 0x8003020; bufs[10][sample] += -2 * ch7.car.calc_slot_snare(noise_bit); } } unsigned old_cphase7 = ch7.mod.cphase; unsigned old_cphase8 = ch8.car.cphase; if (channelActiveBits & (1 << 8)) { for (unsigned sample = 0; sample < num; ++sample) { unsigned phase7 = ch7.mod.calc_phase(0); unsigned phase8 = ch8.car.calc_phase(0); bufs[11][sample] += -2 * ch8.car.calc_slot_cym(phase7, phase8); } } if (channelActiveBits & (1 << (7 + 9))) { // restore noise, ch7/8 cphase noise_seed = old_noise; ch7.mod.cphase = old_cphase7; ch8.car.cphase = old_cphase8; for (unsigned sample = 0; sample < num; ++sample) { noise_seed >>= 1; bool noise_bit = noise_seed & 1; if (noise_bit) noise_seed ^= 0x8003020; unsigned phase7 = ch7.mod.calc_phase(0); unsigned phase8 = ch8.car.calc_phase(0); bufs[12][sample] += 2 * ch7.mod.calc_slot_hat(phase7, phase8, noise_bit); } } if (channelActiveBits & (1 << (8 + 9))) { for (unsigned sample = 0; sample < num; ++sample) { bufs[13][sample] += 2 * ch8.mod.calc_slot_tom(); } } } } void YM2413::writeReg(byte r, byte data) { assert(r < 0x40); switch (r) { case 0x00: { reg[r] = data; patches[0][0].AMPM = (data >> 6) & 3; patches[0][0].EG = (data >> 5) & 1; patches[0][0].setKR ((data >> 4) & 1); patches[0][0].setML ((data >> 0) & 15); unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; ++i) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize if ((ch.mod.state == SUSHOLD) && (ch.mod.patch.EG == 0)) { ch.mod.setEnvelopeState(SUSTAIN); } unsigned freq = getFreq(i); ch.mod.updatePG (freq); ch.mod.updateRKS(freq); ch.mod.updateEG(); } } break; } case 0x01: { reg[r] = data; patches[0][1].AMPM = (data >> 6) & 3; patches[0][1].EG = (data >> 5) & 1; patches[0][1].setKR ((data >> 4) & 1); patches[0][1].setML ((data >> 0) & 15); unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; ++i) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize if ((ch.car.state == SUSHOLD) && (ch.car.patch.EG == 0)) { ch.car.setEnvelopeState(SUSTAIN); } unsigned freq = getFreq(i); ch.car.updatePG (freq); ch.car.updateRKS(freq); ch.car.updateEG(); } } break; } case 0x02: { reg[r] = data; patches[0][0].setKL((data >> 6) & 3); patches[0][0].setTL((data >> 0) & 63); unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; ++i) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize bool actAsCarrier = (i >= 7) && isRhythm(); assert(!actAsCarrier); (void)actAsCarrier; ch.mod.updateTLL(getFreq(i), false); } } break; } case 0x03: { reg[r] = data; patches[0][1].setKL((data >> 6) & 3); patches[0][1].setWF((data >> 4) & 1); patches[0][0].setWF((data >> 3) & 1); patches[0][0].setFB((data >> 0) & 7); unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; ++i) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize } } break; } case 0x04: { reg[r] = data; patches[0][0].AR = (data >> 4) & 15; patches[0][0].DR = (data >> 0) & 15; unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; ++i) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize ch.mod.updateEG(); if (ch.mod.state == ATTACK) { ch.mod.setEnvelopeState(ATTACK); } } } break; } case 0x05: { reg[r] = data; patches[0][1].AR = (data >> 4) & 15; patches[0][1].DR = (data >> 0) & 15; unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; ++i) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize ch.car.updateEG(); if (ch.car.state == ATTACK) { ch.car.setEnvelopeState(ATTACK); } } } break; } case 0x06: { reg[r] = data; patches[0][0].setSL((data >> 4) & 15); patches[0][0].RR = (data >> 0) & 15; unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; ++i) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize ch.mod.updateEG(); if (ch.mod.state == DECAY) { ch.mod.setEnvelopeState(DECAY); } } } break; } case 0x07: { reg[r] = data; patches[0][1].setSL((data >> 4) & 15); patches[0][1].RR = (data >> 0) & 15; unsigned m = isRhythm() ? 6 : 9; for (unsigned i = 0; i < m; i++) { if ((reg[0x30 + i] & 0xF0) == 0) { Channel& ch = channels[i]; ch.setPatch(0, *this); // TODO optimize ch.car.updateEG(); if (ch.car.state == DECAY) { ch.car.setEnvelopeState(DECAY); } } } break; } case 0x0E: { byte old = reg[r]; reg[r] = data; setRhythmFlags(old); break; } case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: r -= 9; // verified on real YM2413 // fall-through case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: { reg[r] = data; unsigned cha = r & 0x0F; assert(cha < 9); Channel& ch = channels[cha]; bool actAsCarrier = (cha >= 7) && isRhythm(); unsigned freq = getFreq(cha); ch.mod.updateAll(freq, actAsCarrier); ch.car.updateAll(freq, true); break; } case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F: r -= 9; // verified on real YM2413 // fall-through case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: { reg[r] = data; unsigned cha = r & 0x0F; assert(cha < 9); Channel& ch = channels[cha]; bool modActAsCarrier = (cha >= 7) && isRhythm(); ch.setSustain((data >> 5) & 1, modActAsCarrier); if (data & 0x10) { ch.keyOn(); } else { ch.keyOff(); } unsigned freq = getFreq(cha); ch.mod.updateAll(freq, modActAsCarrier); ch.car.updateAll(freq, true); break; } case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F: r -= 9; // verified on real YM2413 // fall-through case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: { reg[r] = data; unsigned cha = r & 0x0F; assert(cha < 9); Channel& ch = channels[cha]; if (isRhythm() && (cha >= 6)) { if (cha > 6) { // channel 7 or 8 in ryhthm mode ch.mod.setVolume(data >> 4); } } else { ch.setPatch(data >> 4, *this); } ch.car.setVolume(data & 15); bool actAsCarrier = (cha >= 7) && isRhythm(); unsigned freq = getFreq(cha); ch.mod.updateAll(freq, actAsCarrier); ch.car.updateAll(freq, true); break; } default: break; } } byte YM2413::peekReg(byte r) const { return reg[r]; } } // namespace YM2413Okazaki static enum_string envelopeStateInfo[] = { { "ATTACK", YM2413Okazaki::ATTACK }, { "DECAY", YM2413Okazaki::DECAY }, { "SUSHOLD", YM2413Okazaki::SUSHOLD }, { "SUSTAIN", YM2413Okazaki::SUSTAIN }, { "RELEASE", YM2413Okazaki::RELEASE }, { "SETTLE", YM2413Okazaki::SETTLE }, { "FINISH", YM2413Okazaki::FINISH } }; SERIALIZE_ENUM(YM2413Okazaki::EnvelopeState, envelopeStateInfo); namespace YM2413Okazaki { // version 1: initial version // version 2: don't serialize "type / actAsCarrier" anymore, it's now // a calculated value // version 3: don't serialize slot_on_flag anymore // version 4: don't serialize volume anymore template void Slot::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("feedback", feedback); ar.serialize("output", output); ar.serialize("cphase", cphase); ar.serialize("state", state); ar.serialize("eg_phase", eg_phase); ar.serialize("sustain", sustain); // These are restored by calls to // updateAll(): eg_dphase, dphaseDRTableRks, tll, dphase // setEnvelopeState(): eg_phase_max // setPatch(): patch // setVolume(): volume // update_key_status(): slot_on_flag } // version 1: initial version // version 2: removed patch_number, freq template void Channel::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("mod", mod); ar.serialize("car", car); } // version 1: initial version // version 2: 'registers' are moved here (no longer serialized in base class) // version 3: no longer serialize 'user_patch_mod' and 'user_patch_car' template void YM2413::serialize(Archive& ar, unsigned version) { if (ar.versionBelow(version, 2)) ar.beginTag("YM2413Core"); ar.serialize("registers", reg); if (ar.versionBelow(version, 2)) ar.endTag("YM2413Core"); // no need to serialize patches[] // patches[0] is restored from registers, the others are read-only ar.serialize("channels", channels); ar.serialize("pm_phase", pm_phase); ar.serialize("am_phase", am_phase); ar.serialize("noise_seed", noise_seed); // don't serialize idleSamples, is only an optimization if (ar.isLoader()) { patches[0][0].initModulator(®[0]); patches[0][1].initCarrier (®[0]); for (int i = 0; i < 9; ++i) { Channel& ch = channels[i]; // restore patch unsigned p = ((i >= 6) && isRhythm()) ? (16 + (i - 6)) : (reg[0x30 + i] >> 4); ch.setPatch(p, *this); // before updateAll() // restore volume ch.car.setVolume(reg[0x30 + i] & 15); if (isRhythm() && (i >= 7)) { // ch 7/8 ryhthm ch.mod.setVolume(reg[0x30 + i] >> 4); } // sync various variables bool actAsCarrier = (i >= 7) && isRhythm(); unsigned freq = getFreq(i); ch.mod.updateAll(freq, actAsCarrier); ch.car.updateAll(freq, true); ch.mod.setEnvelopeState(ch.mod.state); ch.car.setEnvelopeState(ch.car.state); } update_key_status(); } } } // namespace YM2413Okazaki using YM2413Okazaki::YM2413; INSTANTIATE_SERIALIZE_METHODS(YM2413); REGISTER_POLYMORPHIC_INITIALIZER(YM2413Core, YM2413, "YM2413-Okazaki"); } // namespace openmsx openmsx-0.10.0/src/sound/WavData.hh0000644000175000017500000000122712262345041017635 0ustar manuelmanuel00000000000000#ifndef WAVDATA_HH #define WAVDATA_HH #include "MemBuffer.hh" #include "openmsx.hh" #include namespace openmsx { class WavData { public: /** Construct empty wav. */ WavData() : length(0) {} /** Construct from .wav file, optionally convert to a specific * bit-depth and sample rate. */ WavData(const std::string& filename, unsigned bits = 0, unsigned freq = 0); unsigned getFreq() const; unsigned getBits() const; unsigned getSize() const; unsigned getChannels() const; const void* getData() const; private: MemBuffer buffer; unsigned bits; unsigned freq; unsigned length; unsigned channels; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/ResampleHQ.hh0000644000175000017500000000151712262345041020311 0ustar manuelmanuel00000000000000#ifndef RESAMPLEHQ_HH #define RESAMPLEHQ_HH #include "ResampleAlgo.hh" #include "DynamicClock.hh" #include namespace openmsx { class ResampledSoundDevice; template class ResampleHQ : public ResampleAlgo { public: ResampleHQ(ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate); virtual ~ResampleHQ(); virtual bool generateOutput(int* dataOut, unsigned num, EmuTime::param time); private: void calcOutput(float pos, int* output); void prepareData(unsigned emuNum); ResampledSoundDevice& input; const DynamicClock& hostClock; DynamicClock emuClock; const float ratio; unsigned bufStart; unsigned bufEnd; unsigned nonzeroSamples; unsigned filterLen; std::vector buffer; float* table; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXOPL3Cartridge.hh0000644000175000017500000000124312262345041021236 0ustar manuelmanuel00000000000000#ifndef MSXOPL3CARTRIDGE_HH #define MSXOPL3CARTRIDGE_HH #include "MSXDevice.hh" #include namespace openmsx { class YMF262; class MSXOPL3Cartridge : public MSXDevice { public: explicit MSXOPL3Cartridge(const DeviceConfig& config); virtual ~MSXOPL3Cartridge(); virtual void reset(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: const std::unique_ptr ymf262; int opl3latch; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXFmPac.cc0000644000175000017500000000716112262345041017655 0ustar manuelmanuel00000000000000#include "MSXFmPac.hh" #include "RomBlockDebuggable.hh" #include "SRAM.hh" #include "Rom.hh" #include "CacheLine.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { static const char* const PAC_Header = "PAC2 BACKUP DATA"; MSXFmPac::MSXFmPac(const DeviceConfig& config) : MSXMusic(config) , sram(make_unique( getName() + " SRAM", 0x1FFE, config, PAC_Header)) , romBlockDebug(make_unique( *this, &bank, 0x4000, 0x4000, 14)) { reset(getCurrentTime()); } MSXFmPac::~MSXFmPac() { } void MSXFmPac::reset(EmuTime::param time) { MSXMusic::reset(time); enable = 0; sramEnabled = false; bank = 0; r1ffe = r1fff = 0; // actual value doesn't matter as long // as it's not the magic combination } void MSXFmPac::writeIO(word port, byte value, EmuTime::param time) { if (enable & 1) { MSXMusic::writeIO(port, value, time); } } byte MSXFmPac::readMem(word address, EmuTime::param /*time*/) { address &= 0x3FFF; switch (address) { case 0x3FF6: return enable; case 0x3FF7: return bank; default: if (sramEnabled) { if (address < 0x1FFE) { return (*sram)[address]; } else if (address == 0x1FFE) { return r1ffe; // always 0x4D } else if (address == 0x1FFF) { return r1fff; // always 0x69 } else { return 0xFF; } } else { return (*rom)[bank * 0x4000 + address]; } } } const byte* MSXFmPac::getReadCacheLine(word address) const { address &= 0x3FFF; if (address == (0x3FF6 & CacheLine::HIGH)) { return nullptr; } if (sramEnabled) { if (address < (0x1FFE & CacheLine::HIGH)) { return &(*sram)[address]; } else if (address == (0x1FFE & CacheLine::HIGH)) { return nullptr; } else { return unmappedRead; } } else { return &(*rom)[bank * 0x4000 + address]; } } void MSXFmPac::writeMem(word address, byte value, EmuTime::param time) { // 'enable' has no effect for memory mapped access // (thanks to BiFiMSX for investigating this) address &= 0x3FFF; switch (address) { case 0x1FFE: if (!(enable & 0x10)) { r1ffe = value; checkSramEnable(); } break; case 0x1FFF: if (!(enable & 0x10)) { r1fff = value; checkSramEnable(); } break; case 0x3FF4: writeRegisterPort(value, time); break; case 0x3FF5: writeDataPort(value, time); break; case 0x3FF6: enable = value & 0x11; if (enable & 0x10) { r1ffe = r1fff = 0; // actual value not important checkSramEnable(); } break; case 0x3FF7: { byte newBank = value & 0x03; if (bank != newBank) { bank = newBank; invalidateMemCache(0x0000, 0x10000); } break; } default: if (sramEnabled && (address < 0x1FFE)) { sram->write(address, value); } } } byte* MSXFmPac::getWriteCacheLine(word address) const { address &= 0x3FFF; if (address == (0x1FFE & CacheLine::HIGH)) { return nullptr; } if (address == (0x3FF4 & CacheLine::HIGH)) { return nullptr; } if (sramEnabled && (address < 0x1FFE)) { return nullptr; } else { return unmappedWrite; } } void MSXFmPac::checkSramEnable() { bool newEnabled = (r1ffe == 0x4D) && (r1fff == 0x69); if (sramEnabled != newEnabled) { sramEnabled = newEnabled; invalidateMemCache(0x0000, 0x10000); } } template void MSXFmPac::serialize(Archive& ar, unsigned version) { ar.template serializeInlinedBase(*this, version); ar.serialize("sram", *sram); ar.serialize("enable", enable); ar.serialize("bank", bank); ar.serialize("r1ffe", r1ffe); ar.serialize("r1fff", r1fff); if (ar.isLoader()) { // sramEnabled can be calculated checkSramEnable(); } } INSTANTIATE_SERIALIZE_METHODS(MSXFmPac); REGISTER_MSXDEVICE(MSXFmPac, "FM-PAC"); } // namespace openmsx openmsx-0.10.0/src/sound/DummyY8950KeyboardDevice.cc0000644000175000017500000000105012262345041022641 0ustar manuelmanuel00000000000000#include "DummyY8950KeyboardDevice.hh" namespace openmsx { void DummyY8950KeyboardDevice::write(byte /*data*/, EmuTime::param /*time*/) { // ignore data } byte DummyY8950KeyboardDevice::read(EmuTime::param /*time*/) { return 255; } string_ref DummyY8950KeyboardDevice::getDescription() const { return ""; } void DummyY8950KeyboardDevice::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { } void DummyY8950KeyboardDevice::unplugHelper(EmuTime::param /*time*/) { } } // namespace openmsx openmsx-0.10.0/src/sound/MSXYamahaSFG.cc0000644000175000017500000000700712262345041020426 0ustar manuelmanuel00000000000000#include "MSXYamahaSFG.hh" #include "YM2151.hh" #include "YM2148.hh" #include "Rom.hh" #include "CacheLine.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { MSXYamahaSFG::MSXYamahaSFG(const DeviceConfig& config) : MSXDevice(config) , rom(make_unique(getName() + " ROM", "rom", config)) , ym2151(make_unique( getName(), "Yamaha SFG-01/05", config, getCurrentTime())) , ym2148(make_unique()) { reset(getCurrentTime()); } MSXYamahaSFG::~MSXYamahaSFG() { } void MSXYamahaSFG::reset(EmuTime::param time) { ym2151->reset(time); ym2148->reset(); registerLatch = 0; // TODO check irqVector = 255; // TODO check } void MSXYamahaSFG::writeMem(word address, byte value, EmuTime::param time) { if (address < 0x3FF0 || address >= 0x3FF8) { return; } switch (address & 0x3FFF) { case 0x3FF0: // OPM ADDRESS REGISTER writeRegisterPort(value, time); break; case 0x3FF1: // OPM DATA REGISTER writeDataPort(value, time); break; case 0x3FF2: // Register for data latched to ST0 to ST7 output ports // TODO: keyboardLatch = value; //std::cerr << "TODO: keyboardLatch = " << (int)value << std::endl; break; case 0x3FF3: // MIDI IRQ VECTOR ADDRESS REGISTER ym2148->setVector(value); break; case 0x3FF4: // EXTERNAL IRQ VECTOR ADDRESS REGISTER // IRQ vector for YM2151 (+ default vector ???) irqVector = value; break; case 0x3FF5: // MIDI standard UART DATA READ BUFFER ym2148->writeData(value); break; case 0x3FF6: // MIDI standard UART STATUS REGISTER ym2148->writeCommand(value); break; } } byte* MSXYamahaSFG::getWriteCacheLine(word start) const { if ((start & CacheLine::HIGH) == (0x3FF0 & CacheLine::HIGH)) { return nullptr; } return unmappedWrite; } byte MSXYamahaSFG::readIRQVector() { return irqVector; } void MSXYamahaSFG::writeRegisterPort(byte value, EmuTime::param /*time*/) { registerLatch = value; } void MSXYamahaSFG::writeDataPort(byte value, EmuTime::param time) { //PRT_DEBUG("YM2151: reg "<<(int)registerLatch<<" val "<<(int)value); ym2151->writeReg(registerLatch, value, time); } byte MSXYamahaSFG::readMem(word address, EmuTime::param time) { return peekMem(address, time); } byte MSXYamahaSFG::peekMem(word address, EmuTime::param /*time*/) const { if (address < 0x3FF0 || address >= 0x3FF8) { // size can also be 16kB for SFG-01 or 32kB for SFG-05 return (*rom)[address & (rom->getSize() - 1)]; } switch (address & 0x3FFF) { case 0x3FF0: // OPM STATUS REGISTER return ym2151->readStatus(); case 0x3FF1: // OPM DATA REGISTER return ym2151->readStatus(); case 0x3FF2: // Data buffer for SD0 to SD7 input ports // TODO: return getKbdStatus(); break; case 0x3FF5: // MIDI standard UART DATA READ BUFFER // For now disabled, as it breaks working functionality (YRM-104) // return ym2148->readData(); break; case 0x3FF6: // MIDI standard UART STATUS REGISTER // For now disabled, as it breaks working functionality (YRM-104) //return ym2148->readStatus(); break; } return 0xFF; } const byte* MSXYamahaSFG::getReadCacheLine(word start) const { if ((start & CacheLine::HIGH) == (0x3FF0 & CacheLine::HIGH)) { return nullptr; } return &(*rom)[start & (rom->getSize() - 1)]; } template void MSXYamahaSFG::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("YM2151", *ym2151); ar.serialize("YM2148", *ym2148); ar.serialize("registerLatch", registerLatch); ar.serialize("irqVector", irqVector); } INSTANTIATE_SERIALIZE_METHODS(MSXYamahaSFG); REGISTER_MSXDEVICE(MSXYamahaSFG, "YamahaSFG"); } // namespace openmsx openmsx-0.10.0/src/sound/SDLSoundDriver.hh0000644000175000017500000000166512262345041021123 0ustar manuelmanuel00000000000000#ifndef SDLSOUNDDRIVER_HH #define SDLSOUNDDRIVER_HH #include "SoundDriver.hh" #include "MemBuffer.hh" #include "openmsx.hh" #include "noncopyable.hh" namespace openmsx { class Reactor; class SDLSoundDriver : public SoundDriver, private noncopyable { public: SDLSoundDriver(Reactor& reactor, unsigned frequency, unsigned samples); virtual ~SDLSoundDriver(); virtual void mute(); virtual void unmute(); virtual unsigned getFrequency() const; virtual unsigned getSamples() const; virtual void uploadBuffer(short* buffer, unsigned len); private: void reInit(); unsigned getBufferFilled() const; unsigned getBufferFree() const; static void audioCallbackHelper(void* userdata, byte* strm, int len); void audioCallback(short* stream, unsigned len); Reactor& reactor; MemBuffer mixBuffer; unsigned frequency; unsigned fragmentSize; unsigned readIdx, writeIdx; bool muted; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/generateYM2413OkazakiTable.cc0000644000175000017500000002270612262345041023135 0ustar manuelmanuel00000000000000#include #include #include #include #include using namespace std; // Constants #include "YM2413OkazakiConfig.hh" static void makeChecks() { cout << "// This is a generated file. DO NOT EDIT!\n" "\n" "// These tables were generated for the following constants:\n" "static_assert(PM_FP_BITS == " << PM_FP_BITS << ", \"mismatch, regenerate\");\n" "static_assert(EP_FP_BITS == " << EP_FP_BITS << ", \"mismatch, regenerate\");\n" "static_assert(DB_BITS == " << DB_BITS << ", \"mismatch, regenerate\");\n" "static_assert(DB_MUTE == " << DB_MUTE << ", \"mismatch, regenerate\");\n" "static_assert(DBTABLEN == " << DBTABLEN << ", \"mismatch, regenerate\");\n" "//static_assert(DB_STEP == " << DB_STEP << ", \"mismatch, regenerate\");\n" "//static_assert(EG_STEP == " << EG_STEP << ", \"mismatch, regenerate\");\n" "static_assert(PG_BITS == " << PG_BITS << ", \"mismatch, regenerate\");\n" "static_assert(PG_WIDTH == " << PG_WIDTH << ", \"mismatch, regenerate\");\n" "static_assert(EG_BITS == " << EG_BITS << ", \"mismatch, regenerate\");\n" "static_assert(DB2LIN_AMP_BITS == " << DB2LIN_AMP_BITS << ", \"mismatch, regenerate\");\n" "static_assert(LFO_AM_TAB_ELEMENTS == " << LFO_AM_TAB_ELEMENTS << ", \"mismatch, regenerate\");\n" "\n"; } template static void formatTable(const T (&tab)[N], unsigned columns, int width) { assert((N % columns) == 0); for (unsigned i = 0; i < N; ++i) { if ((i % columns) == 0) cout << '\t'; cout << setw(width) << tab[i] << (((i % columns) != (columns - 1)) ? "," : ",\n"); } cout << "};\n" "\n"; } template static void formatTable2(const T (&tab)[M][N], int width, int newLines, int columns) { assert((N % columns) == 0); for (unsigned i = 0; i < M; ++i) { if (i && ((i % newLines) == 0)) cout << '\n'; cout << "\t{ "; for (unsigned j = 0; j < N; ++j) { if (j && ((j % columns) == 0)) cout << "\n\t "; cout << setw(width) << tab[i][j]; if (j != (N - 1)) cout << ','; } cout << " },\n"; } cout << "};\n" "\n"; } // Sawtooth function with amplitude 1 and period 1. static inline double saw(double phase) { if (phase < 0.25) { return phase * 4.0; } else if (phase < 0.75) { return 2.0 - (phase * 4.0); } else { return -4.0 + (phase * 4.0); } } static void makePmTable() { cout << "// LFO Phase Modulation table (copied from Burczynski core)\n" "static const signed char pmTable[8][8] =\n" "{\n" " { 0, 0, 0, 0, 0, 0, 0, 0, }, // FNUM = 000xxxxxx\n" " { 0, 0, 1, 0, 0, 0,-1, 0, }, // FNUM = 001xxxxxx\n" " { 0, 1, 2, 1, 0,-1,-2,-1, }, // FNUM = 010xxxxxx\n" " { 0, 1, 3, 1, 0,-1,-3,-1, }, // FNUM = 011xxxxxx\n" " { 0, 2, 4, 2, 0,-2,-4,-2, }, // FNUM = 100xxxxxx\n" " { 0, 2, 5, 2, 0,-2,-5,-2, }, // FNUM = 101xxxxxx\n" " { 0, 3, 6, 3, 0,-3,-6,-3, }, // FNUM = 110xxxxxx\n" " { 0, 3, 7, 3, 0,-3,-7,-3, }, // FNUM = 111xxxxxx\n" "};\n" "\n"; } static void makeDB2LinTable() { int dB2LinTab[2 * DBTABLEN]; for (int i = 0; i < DB_MUTE; ++i) { dB2LinTab[i] = int(double((1 << DB2LIN_AMP_BITS) - 1) * pow(10, -double(i) * DB_STEP / 20)); } dB2LinTab[DB_MUTE - 1] = 0; for (int i = DB_MUTE; i < DBTABLEN; ++i) { dB2LinTab[i] = 0; } for (int i = 0; i < DBTABLEN; ++i) { dB2LinTab[i + DBTABLEN] = -dB2LinTab[i]; } cout << "// dB to linear table (used by Slot)\n" "// dB(0 .. DB_MUTE-1) -> linear(0 .. DB2LIN_AMP_WIDTH)\n" "// indices in range:\n" "// [0, DB_MUTE ) actual values, from max to min\n" "// [DB_MUTE, DBTABLEN) filled with min val (to allow some overflow in index)\n" "// [DBTABLEN, 2*DBTABLEN) as above but for negative output values\n" "static int dB2LinTab[2 * DBTABLEN] = {\n"; formatTable(dB2LinTab, 8, 5); } static void makeAdjustTable() { unsigned AR_ADJUST_TABLE[1 << EG_BITS]; AR_ADJUST_TABLE[0] = (1 << EG_BITS) - 1; for (int i = 1; i < (1 << EG_BITS); ++i) { AR_ADJUST_TABLE[i] = unsigned(double(1 << EG_BITS) - 1 - ((1 << EG_BITS) - 1) * ::log(double(i)) / ::log(127.0)); } cout << "// Linear to Log curve conversion table (for Attack rate)\n" "static unsigned AR_ADJUST_TABLE[1 << EG_BITS] = {\n"; formatTable(AR_ADJUST_TABLE, 8, 4); } static void makeTllTable() { // Processed version of Table III-5 from the Application Manual. static const unsigned kltable[16] = { 0, 24, 32, 37, 40, 43, 45, 47, 48, 50, 51, 52, 53, 54, 55, 56 }; // Note: KL [0..3] results in {0.0, 1.5, 3.0, 6.0} dB/oct. // This is different from Y8950 and YMF262 which have {0, 3, 1.5, 6}. // (2nd and 3rd elements are swapped). Verified on real YM2413. unsigned tllTable[4][16 * 8]; for (unsigned freq = 0; freq < 16 * 8; ++freq) { unsigned fnum = freq & 15; unsigned block = freq / 16; int tmp = 2 * kltable[fnum] - 16 * (7 - block); for (unsigned KL = 0; KL < 4; ++KL) { tllTable[KL][freq] = (tmp <= 0 || KL == 0) ? 0 : (tmp >> (3 - KL)); assert(tllTable[KL][freq] <= 112); } } cout << "// KSL + TL Table values are in range [0, 112]\n" << "static byte tllTable[4][16 * 8] = {\n"; formatTable2(tllTable, 3, 99, 16); } // lin(+0.0 .. +1.0) to dB(DB_MUTE-1 .. 0) static int lin2db(double d) { return (d == 0) ? DB_MUTE - 1 : std::min(-int(20.0 * log10(d) / DB_STEP), DB_MUTE - 1); // 0 - 127 } // Sin Table static void makeSinTable() { unsigned fullsintable[PG_WIDTH]; unsigned halfsintable[PG_WIDTH]; for (int i = 0; i < PG_WIDTH / 4; ++i) { fullsintable[i] = lin2db(sin(2.0 * M_PI * i / PG_WIDTH)); } for (int i = 0; i < PG_WIDTH / 4; ++i) { fullsintable[PG_WIDTH / 2 - 1 - i] = fullsintable[i]; } for (int i = 0; i < PG_WIDTH / 2; ++i) { fullsintable[PG_WIDTH / 2 + i] = DBTABLEN + fullsintable[i]; } for (int i = 0; i < PG_WIDTH / 2; ++i) { halfsintable[i] = fullsintable[i]; } for (int i = PG_WIDTH / 2; i < PG_WIDTH; ++i) { halfsintable[i] = fullsintable[0]; } cout << "// WaveTable for each envelope amp\n" "// values are in range [0, DB_MUTE) (for positive values)\n" "// or [0, DB_MUTE) + DBTABLEN (for negative values)\n" "static unsigned fullsintable[PG_WIDTH] = {\n"; formatTable(fullsintable, 8, 5); cout << "static unsigned halfsintable[PG_WIDTH] = {\n"; formatTable(halfsintable, 8, 5); cout << "static unsigned* waveform[2] = {fullsintable, halfsintable};\n" "\n"; } static void makeDphaseDRTable() { int dphaseDRTable[16][16]; for (unsigned Rks = 0; Rks < 16; ++Rks) { dphaseDRTable[Rks][0] = 0; for (unsigned DR = 1; DR < 16; ++DR) { unsigned RM = std::min(DR + (Rks >> 2), 15u); unsigned RL = Rks & 3; dphaseDRTable[Rks][DR] = ((RL + 4) << EP_FP_BITS) >> (16 - RM); } } cout << "// Phase incr table for attack, decay and release\n" "// note: original code had indices swapped. It also had\n" "// a separate table for attack\n" "// 17.15 fixed point\n" "static int dphaseDRTable[16][16] = {\n"; formatTable2(dphaseDRTable, 6, 999, 8); } static void makeLfoAmTable() { cout << "// LFO Amplitude Modulation table (verified on real YM3812)\n" "static const unsigned char lfo_am_table[LFO_AM_TAB_ELEMENTS] = {\n" " 0,0,0,0,0,0,0,\n" " 1,1,1,1,\n" " 2,2,2,2,\n" " 3,3,3,3,\n" " 4,4,4,4,\n" " 5,5,5,5,\n" " 6,6,6,6,\n" " 7,7,7,7,\n" " 8,8,8,8,\n" " 9,9,9,9,\n" " 10,10,10,10,\n" " 11,11,11,11,\n" " 12,12,12,12,\n" " 13,13,13,13,\n" " 14,14,14,14,\n" " 15,15,15,15,\n" " 16,16,16,16,\n" " 17,17,17,17,\n" " 18,18,18,18,\n" " 19,19,19,19,\n" " 20,20,20,20,\n" " 21,21,21,21,\n" " 22,22,22,22,\n" " 23,23,23,23,\n" " 24,24,24,24,\n" " 25,25,25,25,\n" " 26,26,26,\n" " 25,25,25,25,\n" " 24,24,24,24,\n" " 23,23,23,23,\n" " 22,22,22,22,\n" " 21,21,21,21,\n" " 20,20,20,20,\n" " 19,19,19,19,\n" " 18,18,18,18,\n" " 17,17,17,17,\n" " 16,16,16,16,\n" " 15,15,15,15,\n" " 14,14,14,14,\n" " 13,13,13,13,\n" " 12,12,12,12,\n" " 11,11,11,11,\n" " 10,10,10,10,\n" " 9,9,9,9,\n" " 8,8,8,8,\n" " 7,7,7,7,\n" " 6,6,6,6,\n" " 5,5,5,5,\n" " 4,4,4,4,\n" " 3,3,3,3,\n" " 2,2,2,2,\n" " 1,1,1,1,\n" "};\n" "\n"; } static void makeSusLevTable() { unsigned slTable[16]; for (int i = 0; i < 16; ++i) { double x = (i == 15) ? 48.0 : (3.0 * i); slTable[i] = int(x / EG_STEP) << EP_FP_BITS; } cout << "// Sustain level (17.15 fixed point)\n" "static const unsigned slTable[16] = {\n"; formatTable(slTable, 4, 8); } static void makeMLTable() { cout << "// ML-table\n" "static const byte mlTable[16] = {\n" " 1, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2, 7*2,\n" " 8*2, 9*2, 10*2, 10*2, 12*2, 12*2, 15*2, 15*2\n" "};\n" "\n"; } int main() { makeChecks(); makePmTable(); makeDB2LinTable(); makeAdjustTable(); makeTllTable(); makeSinTable(); makeDphaseDRTable(); makeLfoAmTable(); makeSusLevTable(); makeMLTable(); } openmsx-0.10.0/src/sound/SoundDevice.cc0000644000175000017500000002004712262345041020505 0ustar manuelmanuel00000000000000#include "SoundDevice.hh" #include "MSXMixer.hh" #include "DeviceConfig.hh" #include "XMLElement.hh" #include "WavWriter.hh" #include "Filename.hh" #include "StringOp.hh" #include "MemoryOps.hh" #include "MemBuffer.hh" #include "MSXException.hh" #include "aligned.hh" #include "likely.hh" #include "vla.hh" #include "unreachable.hh" #include "memory.hh" #include #include using std::string; namespace openmsx { MemBuffer mixBufferStorage; int* mixBuffer; // 16-byte aligned ptr into mixBufferStorage (for SSE access) static void allocateMixBuffer(unsigned size) { size += 3; // to be able to align if (unlikely(mixBufferStorage.size() < size)) { mixBufferStorage.resize(size); // align at 16-byte boundary auto tmp = reinterpret_cast(mixBufferStorage.data()); mixBuffer = reinterpret_cast((tmp + 15) & ~15); } } static string makeUnique(MSXMixer& mixer, string_ref name) { string result = name.str(); if (mixer.findDevice(result)) { unsigned n = 0; do { result = StringOp::Builder() << name << " (" << ++n << ')'; } while (mixer.findDevice(result)); } return result; } SoundDevice::SoundDevice(MSXMixer& mixer_, string_ref name_, string_ref description_, unsigned numChannels_, bool stereo_) : mixer(mixer_) , name(makeUnique(mixer, name_)) , description(description_.str()) , numChannels(numChannels_) , stereo(stereo_ ? 2 : 1) , numRecordChannels(0) , balanceCenter(true) { assert(numChannels <= MAX_CHANNELS); assert(stereo == 1 || stereo == 2); // initially no channels are muted for (unsigned i = 0; i < numChannels; ++i) { channelMuted[i] = false; channelBalance[i] = 0; } } SoundDevice::~SoundDevice() { } const std::string& SoundDevice::getName() const { return name; } const std::string& SoundDevice::getDescription() const { return description; } bool SoundDevice::isStereo() const { return stereo == 2 || !balanceCenter; } int SoundDevice::getAmplificationFactor() const { return 1; } void SoundDevice::registerSound(const DeviceConfig& config) { const XMLElement& soundConfig = config.getChild("sound"); double volume = soundConfig.getChildDataAsInt("volume") / 32767.0; int devBalance = 0; string_ref mode = soundConfig.getChildData("mode", "mono"); if (mode == "mono") { devBalance = 0; } else if (mode == "left") { devBalance = -100; } else if (mode == "right") { devBalance = 100; } else { throw MSXException("balance \"" + mode + "\" illegal"); } for (auto& b : soundConfig.getChildren("balance")) { int balance = b->getDataAsInt(); if (!b->hasAttribute("channel")) { devBalance = balance; continue; } // TODO Support other balances if (balance != 0 && balance != -100 && balance != 100) { throw MSXException(StringOp::Builder() << "balance " << balance << " illegal"); } if (balance != 0) { balanceCenter = false; } const string& range = b->getAttribute("channel"); for (unsigned c : StringOp::parseRange(range, 1, numChannels)) { channelBalance[c - 1] = balance; } } mixer.registerSound(*this, volume, devBalance, numChannels); } void SoundDevice::unregisterSound() { mixer.unregisterSound(*this); } void SoundDevice::updateStream(EmuTime::param time) { mixer.updateStream(time); } void SoundDevice::setInputRate(unsigned sampleRate) { inputSampleRate = sampleRate; } void SoundDevice::recordChannel(unsigned channel, const Filename& filename) { assert(channel < numChannels); bool wasRecording = writer[channel] != nullptr; if (!filename.empty()) { writer[channel] = make_unique( filename, stereo, inputSampleRate); } else { writer[channel].reset(); } bool recording = writer[channel] != nullptr; if (recording != wasRecording) { if (recording) { if (numRecordChannels == 0) { mixer.setSynchronousMode(true); } ++numRecordChannels; assert(numRecordChannels <= numChannels); } else { assert(numRecordChannels > 0); --numRecordChannels; if (numRecordChannels == 0) { mixer.setSynchronousMode(false); } } } } void SoundDevice::muteChannel(unsigned channel, bool muted) { assert(channel < numChannels); channelMuted[channel] = muted; } bool SoundDevice::mixChannels(int* dataOut, unsigned samples) { #ifdef __SSE2__ assert((long(dataOut) & 15) == 0); // must be 16-byte aligned #endif if (samples == 0) return true; unsigned outputStereo = isStereo() ? 2 : 1; MemoryOps::MemSet mset; mset(reinterpret_cast(dataOut), outputStereo * samples, 0); VLA(int*, bufs, numChannels); unsigned separateChannels = 0; unsigned pitch = (samples * stereo + 3) & ~3; // align for SSE access // TODO optimization: All channels with the same balance (according to // channelBalance[]) could use the same buffer when balanceCenter is // false for (unsigned i = 0; i < numChannels; ++i) { if (!channelMuted[i] && !writer[i] && balanceCenter) { // no need to keep this channel separate bufs[i] = dataOut; } else { // muted or recorded channels must go separate // cannot yet fill in bufs[i] here ++separateChannels; } } if (separateChannels) { allocateMixBuffer(pitch * separateChannels); mset(reinterpret_cast(mixBuffer), pitch * separateChannels, 0); // still need to fill in (some) bufs[i] pointers unsigned count = 0; for (unsigned i = 0; i < numChannels; ++i) { if (!(!channelMuted[i] && !writer[i] && balanceCenter)) { bufs[i] = &mixBuffer[pitch * count++]; } } assert(count == separateChannels); } // note: some SoundDevices (DACSound16S and CassettePlayer) replace the // (single) channel data instead of adding to the exiting data. // ATM that's ok because the existing data is anyway zero. generateChannels(bufs, samples); if (separateChannels == 0) { for (unsigned i = 0; i < numChannels; ++i) { if (bufs[i]) { return true; } } return false; } // record channels for (unsigned i = 0; i < numChannels; ++i) { if (writer[i]) { assert(bufs[i] != dataOut); if (bufs[i]) { writer[i]->write( bufs[i], stereo, samples, getAmplificationFactor()); } else { writer[i]->writeSilence(stereo, samples); } } } // remove muted channels (explictly by user or by device itself) bool anyUnmuted = false; unsigned numMix = 0; VLA(int, mixBalance, numChannels); for (unsigned i = 0; i < numChannels; ++i) { if (bufs[i] && !channelMuted[i]) { anyUnmuted = true; if (bufs[i] != dataOut) { bufs[numMix] = bufs[i]; mixBalance[numMix] = channelBalance[i]; ++numMix; } } } if (numMix == 0) { // all extra channels muted return anyUnmuted; } // actually mix channels if (!balanceCenter) { unsigned i = 0; do { int left0 = 0; int right0 = 0; int left1 = 0; int right1 = 0; unsigned j = 0; do { if (mixBalance[j] <= 0) { left0 += bufs[j][i + 0]; left1 += bufs[j][i + 1]; } if (mixBalance[j] >= 0) { right0 += bufs[j][i + 0]; right1 += bufs[j][i + 1]; } j++; } while (j < numMix); dataOut[i * 2 + 0] = left0; dataOut[i * 2 + 1] = right0; dataOut[i * 2 + 2] = left1; dataOut[i * 2 + 3] = right1; i += 2; } while (i < samples); return true; } // In the past we had ARM and x86-SSE2 optimized assembly routines for // the stuff below. Currently this code is only rarely used anymore // (only when recording or muting individual soundchip channels), so // it's not worth the extra complexity anymore. unsigned num = samples * stereo; unsigned i = 0; do { int out0 = dataOut[i + 0]; int out1 = dataOut[i + 1]; int out2 = dataOut[i + 2]; int out3 = dataOut[i + 3]; unsigned j = 0; do { out0 += bufs[j][i + 0]; out1 += bufs[j][i + 1]; out2 += bufs[j][i + 2]; out3 += bufs[j][i + 3]; ++j; } while (j < numMix); dataOut[i + 0] = out0; dataOut[i + 1] = out1; dataOut[i + 2] = out2; dataOut[i + 3] = out3; i += 4; } while (i < num); return true; } const DynamicClock& SoundDevice::getHostSampleClock() const { return mixer.getHostSampleClock(); } double SoundDevice::getEffectiveSpeed() const { return mixer.getEffectiveSpeed(); } } // namespace openmsx openmsx-0.10.0/src/sound/YM2413OkazakiConfig.hh0000644000175000017500000000353612262345041021612 0ustar manuelmanuel00000000000000#ifndef YM2413OKAZAKICONFIG_HH #define YM2413OKAZAKICONFIG_HH // Number of bits in 'PhaseModulation' and 'EnvPhaseIndex' fixed point types. static const int PM_FP_BITS = 8; static const int EP_FP_BITS = 15; // Dynamic range (Accuracy of sin table) static const int DB_BITS = 8; static const int DB_MUTE = 1 << DB_BITS; static const int DBTABLEN = 3 * DB_MUTE; // enough to not have to check for overflow static const double DB_STEP = 48.0 / DB_MUTE; static const double EG_STEP = 0.375; static const double TL_STEP = 0.75; // Size of Sintable ( 8 -- 18 can be used, but 9 recommended.) static const int PG_BITS = 9; static const int PG_WIDTH = 1 << PG_BITS; static const int PG_MASK = PG_WIDTH - 1; // Phase increment counter static const int DP_BITS = 18; static const int DP_BASE_BITS = DP_BITS - PG_BITS; // Dynamic range of envelope static const int EG_BITS = 7; // Bits for linear value static const int DB2LIN_AMP_BITS = 8; static const int SLOT_AMP_BITS = DB2LIN_AMP_BITS; // Bits for Amp modulator static const int AM_PG_BITS = 8; static const int AM_PG_WIDTH = 1 << AM_PG_BITS; static const int AM_DP_BITS = 16; static const int AM_DP_WIDTH = 1 << AM_DP_BITS; static const int AM_DP_MASK = AM_DP_WIDTH - 1; // LFO Amplitude Modulation table (verified on real YM3812) // 27 output levels (triangle waveform); // 1 level takes one of: 192, 256 or 448 samples // // Length: 210 elements. // Each of the elements has to be repeated // exactly 64 times (on 64 consecutive samples). // The whole table takes: 64 * 210 = 13440 samples. // // Verified on real YM3812 (OPL2), but I believe it's the same for YM2413 // because it closely matches the YM2413 AM parameters: // speed = 3.7Hz // depth = 4.875dB // Also this approch can be easily implemented in HW, the previous one (see SVN // history) could not. static const unsigned LFO_AM_TAB_ELEMENTS = 210; #endif openmsx-0.10.0/src/sound/EmuTimer.hh0000644000175000017500000000267512262345041020045 0ustar manuelmanuel00000000000000#ifndef EMUTIMER_HH #define EMUTIMER_HH #include "Schedulable.hh" #include "DynamicClock.hh" #include "openmsx.hh" #include namespace openmsx { class EmuTimerCallback { public: virtual void callback(byte value) = 0; protected: virtual ~EmuTimerCallback() {} }; class EmuTimer : public Schedulable { public: EmuTimer(Scheduler& scheduler, EmuTimerCallback& cb, byte flag, unsigned freq_num, unsigned freq_denom, unsigned maxval); static std::unique_ptr createOPM_1( Scheduler& scheduler, EmuTimerCallback& cb); static std::unique_ptr createOPM_2( Scheduler& scheduler, EmuTimerCallback& cb); static std::unique_ptr createOPL3_1( Scheduler& scheduler, EmuTimerCallback& cb); static std::unique_ptr createOPL3_2( Scheduler& scheduler, EmuTimerCallback& cb); static std::unique_ptr createOPL4_1( Scheduler& scheduler, EmuTimerCallback& cb); static std::unique_ptr createOPL4_2( Scheduler& scheduler, EmuTimerCallback& cb); void setValue(int value); void setStart(bool start, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: virtual void executeUntil(EmuTime::param time, int userData); void schedule(EmuTime::param time); void unschedule(); EmuTimerCallback& cb; DynamicClock clock; const unsigned maxval; int count; const byte flag; bool counting; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/DummyAudioInputDevice.cc0000644000175000017500000000070412262345041022510 0ustar manuelmanuel00000000000000#include "DummyAudioInputDevice.hh" namespace openmsx { string_ref DummyAudioInputDevice::getDescription() const { return ""; } void DummyAudioInputDevice::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { } void DummyAudioInputDevice::unplugHelper(EmuTime::param /*time*/) { } short DummyAudioInputDevice::readSample(EmuTime::param /*time*/) { return 0; // silence } } // namespace openmsx openmsx-0.10.0/src/sound/MSXPSG.hh0000644000175000017500000000217712262345041017334 0ustar manuelmanuel00000000000000#ifndef MSXPSG_HH #define MSXPSG_HH #include "MSXDevice.hh" #include "AY8910Periphery.hh" #include "serialize_meta.hh" #include namespace openmsx { class AY8910; class CassettePortInterface; class RenShaTurbo; class JoystickPortIf; class MSXPSG : public MSXDevice, public AY8910Periphery { public: explicit MSXPSG(const DeviceConfig& config); virtual ~MSXPSG(); virtual void reset(EmuTime::param time); virtual void powerDown(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: // AY8910Periphery: port A input, port B output virtual byte readA(EmuTime::param time); virtual void writeB(byte value, EmuTime::param time); std::unique_ptr ay8910; JoystickPortIf* ports[2]; CassettePortInterface& cassette; RenShaTurbo& renShaTurbo; int registerLatch; int selectedPort; byte prev; const bool keyLayoutBit; }; SERIALIZE_CLASS_VERSION(MSXPSG, 2); } // namespace openmsx #endif openmsx-0.10.0/src/sound/ResampleBlip.hh0000644000175000017500000000162012262345041020662 0ustar manuelmanuel00000000000000#ifndef RESAMPLEBLIP_HH #define RESAMPLEBLIP_HH #include "ResampleAlgo.hh" #include "BlipBuffer.hh" #include "DynamicClock.hh" namespace openmsx { class ResampledSoundDevice; template class ResampleBlip : public ResampleAlgo { public: ResampleBlip(ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate); virtual bool generateOutput(int* dataOut, unsigned num, EmuTime::param time); private: BlipBuffer blip[CHANNELS]; ResampledSoundDevice& input; const DynamicClock& hostClock; // time of the last host-sample, // ticks once per host sample DynamicClock emuClock; // time of the last emu-sample, // ticks once per emu-sample typedef FixedPoint<16> FP; const FP step; int lastInput[CHANNELS]; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/Y8950Periphery.hh0000644000175000017500000000336612262345041020742 0ustar manuelmanuel00000000000000#ifndef Y8950PERIPHERY_HH #define Y8950PERIPHERY_HH #include "EmuTime.hh" #include "openmsx.hh" #include #include namespace openmsx { /** Models the 4 general purpose I/O pins on the Y8950 * (controlled by registers r#18 and r#19) */ class Y8950Periphery { public: virtual ~Y8950Periphery(); virtual void reset(); /** Write to (some of) the pins * @param outputs A '1' bit indicates the corresponding bit is * programmed as output. * @param values The actual value that is written, only bits for * which the corresponding bit in the 'outputs' * parameter is set are meaningful. * @param time The moment in time the write occurs */ virtual void write(nibble outputs, nibble values, EmuTime::param time) = 0; /** Read from (some of) the pins * Some of the pins might be programmed as output, but this method * doesn't care about that, it should return the value of all pins * as-if they were all programmed as input. * @param time The moment in time the read occurs */ virtual nibble read(EmuTime::param time) = 0; /** SP-OFF bit (bit 3 in Y8950 register 7) */ virtual void setSPOFF(bool value, EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte* getWriteCacheLine(word start) const; }; class MSXAudio; class DeviceConfig; class Y8950PeripheryFactory { public: static std::unique_ptr create( MSXAudio& audio, const DeviceConfig& config, const std::string& soundDeviceName); }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/SoundDriver.hh0000644000175000017500000000132312262345041020547 0ustar manuelmanuel00000000000000#ifndef SOUNDDRIVER_HH #define SOUNDDRIVER_HH namespace openmsx { class SoundDriver { public: virtual ~SoundDriver() {} /** Mute the sound system */ virtual void mute() = 0; /** Unmute the sound system */ virtual void unmute() = 0; /** Returns the actual sample frequency. This might be different * from the requested frequency ('frequency' setting). */ virtual unsigned getFrequency() const = 0; /** Get the number of samples that should be created 'per fragment'. * This is not the same value as the 'samples setting'. */ virtual unsigned getSamples() const = 0; virtual void uploadBuffer(short* buffer, unsigned len) = 0; protected: SoundDriver() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/Y8950KeyboardDevice.hh0000644000175000017500000000156312262345041021650 0ustar manuelmanuel00000000000000#ifndef Y8950KEYBOARDDEVICE_HH #define Y8950KEYBOARDDEVICE_HH #include "Pluggable.hh" #include "openmsx.hh" namespace openmsx { class Y8950KeyboardDevice : public Pluggable { public: /** * Send data to the device. * Normally this is used to select a certain row from the * keyboard but you might also connect a non-keyboard device. * A 1-bit means corresponding row is selected ( 0V) * 0-bit not selected (+5V) */ virtual void write(byte data, EmuTime::param time) = 0; /** * Read data from the device. * Normally this are the keys that are pressed but you might * also connect a non-keyboard device. * A 0-bit means corresponding key is pressed * 1-bit not pressed */ virtual byte read(EmuTime::param time) = 0; // pluggable virtual string_ref getClass() const; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXSCCPlusCart.hh0000644000175000017500000000263112262345041020764 0ustar manuelmanuel00000000000000// Note: this device is actually called SCC-I. But this would take a lot of // renaming, which isn't worth it right now. TODO rename this :) #ifndef MSXSCCPLUSCART_HH #define MSXSCCPLUSCART_HH #include "MSXDevice.hh" #include namespace openmsx { class SCC; class Ram; class RomBlockDebuggable; class MSXSCCPlusCart : public MSXDevice { public: explicit MSXSCCPlusCart(const DeviceConfig& config); virtual ~MSXSCCPlusCart(); virtual void powerUp(EmuTime::param time); virtual void reset(EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte* getWriteCacheLine(word start) const; template void serialize(Archive& ar, unsigned version); private: void setMapper(int regio, byte value); void setModeRegister(byte value); void checkEnable(); const std::unique_ptr ram; const std::unique_ptr scc; const std::unique_ptr romBlockDebug; byte* internalMemoryBank[4]; // 4 blocks of 8kB starting at #4000 enum SCCEnable {EN_NONE, EN_SCC, EN_SCCPLUS} enable; byte modeRegister; bool isRamSegment[4]; bool isMapped[4]; byte mapper[4]; /*const*/ byte mapperMask; /*const*/ bool lowRAM, highRAM; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/BlipBuffer.hh0000644000175000017500000000167312262345041020333 0ustar manuelmanuel00000000000000// Heavily based on: // // Band-limited sound synthesis and buffering // Blip_Buffer 0.4.0 // http://www.slack.net/~ant/ #ifndef BLIPBUFFER_HH #define BLIPBUFFER_HH #include "FixedPoint.hh" namespace openmsx { #include "BlipConfig.hh" class BlipBuffer { public: typedef FixedPoint TimeIndex; BlipBuffer(); // Update amplitude of waveform at given time. Time is in output sample // units and since the last time readSamples() was called. void addDelta(TimeIndex time, int delta); // Read the given amount of samples into destination buffer. template bool readSamples(int* dest, unsigned samples); private: template void readSamplesHelper(int* out, unsigned samples) __restrict; static const unsigned BUFFER_SIZE = 1 << 14; static const unsigned BUFFER_MASK = BUFFER_SIZE - 1; int buffer[BUFFER_SIZE]; unsigned offset; int accum; int availSamp; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/YMF278.hh0000644000175000017500000000142412262345041017201 0ustar manuelmanuel00000000000000#ifndef YMF278_HH #define YMF278_HH #include "EmuTime.hh" #include "openmsx.hh" #include "serialize_meta.hh" #include #include namespace openmsx { class MSXMotherBoard; class DeviceConfig; class YMF278 { public: YMF278(const std::string& name, int ramSize, const DeviceConfig& config); ~YMF278(); void clearRam(); void reset(EmuTime::param time); void writeReg(byte reg, byte data, EmuTime::param time); byte readReg(byte reg); byte peekReg(byte reg) const; byte readMem(unsigned address) const; void writeMem(unsigned address, byte value); template void serialize(Archive& ar, unsigned version); private: class Impl; const std::unique_ptr pimpl; }; SERIALIZE_CLASS_VERSION(YMF278, 3); } // namespace openmsx #endif openmsx-0.10.0/src/sound/SamplePlayer.cc0000644000175000017500000000737012262345041020677 0ustar manuelmanuel00000000000000#include "SamplePlayer.hh" #include "DeviceConfig.hh" #include "MSXCliComm.hh" #include "FileContext.hh" #include "StringOp.hh" #include "MSXException.hh" #include "serialize.hh" #include "memory.hh" #include namespace openmsx { SamplePlayer::SamplePlayer(const std::string& name, const std::string& desc, const DeviceConfig& config, const std::string& samplesBaseName, unsigned numSamples, const std::string& alternativeName) : ResampledSoundDevice(config.getMotherBoard(), name, desc, 1) { setInputRate(44100); // Initialize with dummy value bool alreadyWarned = false; samples.resize(numSamples); // initialize with empty wavs SystemFileContext context; for (unsigned i = 0; i < numSamples; ++i) { try { std::string filename = StringOp::Builder() << samplesBaseName << i << ".wav"; samples[i] = WavData(context.resolve(filename)); } catch (MSXException& e1) { try { if (alternativeName.empty()) throw; std::string filename = StringOp::Builder() << alternativeName << i << ".wav"; samples[i] = WavData(context.resolve(filename)); } catch (MSXException& /*e2*/) { if (!alreadyWarned) { alreadyWarned = true; // print message from the 1st error config.getCliComm().printWarning( "Couldn't read " + name + " sample data: " + e1.getMessage() + ". Continuing without sample data."); } } } } registerSound(config); reset(); // avoid UMR on serialize index = 0; } SamplePlayer::~SamplePlayer() { unregisterSound(); } void SamplePlayer::reset() { currentSampleNum = unsigned(-1); stopRepeat(); } void SamplePlayer::stopRepeat() { nextSampleNum = unsigned(-1); } void SamplePlayer::play(unsigned sampleNum) { assert(sampleNum < samples.size()); currentSampleNum = sampleNum; index = 0; setWavParams(); } void SamplePlayer::setWavParams() { if ((currentSampleNum < samples.size()) && samples[currentSampleNum].getSize()) { auto& wav = samples[currentSampleNum]; sampBuf = wav.getData(); bufferSize = wav.getSize(); unsigned bits = wav.getBits(); assert((bits == 8) || (bits == 16)); bits8 = (bits == 8); unsigned freq = wav.getFreq(); if (freq != getInputRate()) { // this potentially switches resampler, so there might be // some dropped samples if this is done in the middle of // playing, though this shouldn't happen often (or at all) setInputRate(freq); createResampler(); } } else { reset(); } } void SamplePlayer::repeat(unsigned sampleNum) { assert(sampleNum < samples.size()); nextSampleNum = sampleNum; if (!isPlaying()) { doRepeat(); } } bool SamplePlayer::isPlaying() const { return currentSampleNum != unsigned(-1); } inline int SamplePlayer::getSample(unsigned index) { return bits8 ? (static_cast(sampBuf)[index] - 0x80) * 256 : static_cast(sampBuf)[index]; } void SamplePlayer::generateChannels(int** bufs, unsigned num) { if (!isPlaying()) { bufs[0] = nullptr; return; } for (unsigned i = 0; i < num; ++i) { if (index >= bufferSize) { if (nextSampleNum != unsigned(-1)) { doRepeat(); } else { // no need to add 0 to the remainder of bufs[0] // bufs[0][i] += 0; currentSampleNum = unsigned(-1); break; } } int samp = getSample(index++); bufs[0][i] += 3 * samp; } } void SamplePlayer::doRepeat() { play(nextSampleNum); } template void SamplePlayer::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("index", index); ar.serialize("currentSampleNum", currentSampleNum); ar.serialize("nextSampleNum", nextSampleNum); if (ar.isLoader()) { setWavParams(); } } INSTANTIATE_SERIALIZE_METHODS(SamplePlayer); } // namespace openmsx openmsx-0.10.0/src/sound/ResampleAlgo.hh0000644000175000017500000000045112262345041020657 0ustar manuelmanuel00000000000000#ifndef RESAMPLEALGO_HH #define RESAMPLEALGO_HH #include "EmuTime.hh" namespace openmsx { class ResampleAlgo { public: virtual ~ResampleAlgo() {} virtual bool generateOutput(int* dataOut, unsigned num, EmuTime::param time) = 0; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/Y8950.cc0000644000175000017500000011207312262345041017034 0ustar manuelmanuel00000000000000/* * Based on: * emu8950.c -- Y8950 emulator written by Mitsutaka Okazaki 2001 * heavily rewritten to fit openMSX structure */ #include "Y8950.hh" #include "Y8950Adpcm.hh" #include "Y8950KeyboardConnector.hh" #include "Y8950Periphery.hh" #include "MSXAudio.hh" #include "ResampledSoundDevice.hh" #include "EmuTimer.hh" #include "SimpleDebuggable.hh" #include "IRQHelper.hh" #include "DeviceConfig.hh" #include "MSXMotherBoard.hh" #include "DACSound16S.hh" #include "FixedPoint.hh" #include "Math.hh" #include "serialize.hh" #include "memory.hh" #include #include namespace openmsx { // Dynamic range of envelope static const int EG_BITS = 9; static const unsigned EG_MUTE = 1 << EG_BITS; // Bits for envelope phase incremental counter static const int EG_DP_BITS = 23; typedef FixedPoint EnvPhaseIndex; static const EnvPhaseIndex EG_DP_MAX = EnvPhaseIndex(EG_MUTE); class Y8950Debuggable : public SimpleDebuggable { public: Y8950Debuggable(MSXMotherBoard& motherBoard, Y8950& y8950, const std::string& name); virtual byte read(unsigned address, EmuTime::param time); virtual void write(unsigned address, byte value, EmuTime::param time); private: Y8950& y8950; }; class Y8950Patch { public: Y8950Patch(); void reset(); void setKeyScaleRate(bool value) { KR = value ? 9 : 11; } void setFeedbackShift(byte value) { FB = value ? 8 - value : 0; } template void serialize(Archive& ar, unsigned version); bool AM, PM, EG; byte KR; // 0,1 transformed to 9,11 byte ML; // 0-15 byte KL; // 0-3 byte TL; // 0-63 byte FB; // 0,1-7 transformed to 0,7-1 byte AR; // 0-15 byte DR; // 0-15 byte SL; // 0-15 byte RR; // 0-15 }; class Y8950Slot { public: void reset(); inline bool isActive() const; inline void slotOn(); inline void slotOff(); inline unsigned calc_phase(int lfo_pm); inline unsigned calc_envelope(int lfo_am); inline int calc_slot_car(int lfo_pm, int lfo_am, int fm); inline int calc_slot_mod(int lfo_pm, int lfo_am); inline int calc_slot_tom(int lfo_pm, int lfo_am); inline int calc_slot_snare(int lfo_pm, int lfo_am, int whitenoise); inline int calc_slot_cym(int lfo_am, int a, int b); inline int calc_slot_hat(int lfo_am, int a, int b, int whitenoise); inline void updateAll(unsigned freq); inline void updatePG(unsigned freq); inline void updateTLL(unsigned freq); inline void updateRKS(unsigned freq); inline void updateEG(); template void serialize(Archive& ar, unsigned version); // OUTPUT int feedback; int output; // Output value of slot // for Phase Generator (PG) unsigned phase; // Phase unsigned dphase; // Phase increment amount // for Envelope Generator (EG) EnvPhaseIndex* dphaseARTableRks; EnvPhaseIndex* dphaseDRTableRks; int tll; // Total Level + Key scale level int eg_mode; // Current state EnvPhaseIndex eg_phase; // Phase EnvPhaseIndex eg_dphase;// Phase increment amount Y8950Patch patch; bool slotStatus; }; static const unsigned MOD = 0; static const unsigned CAR = 1; class Y8950Channel { public: Y8950Channel(); void reset(); inline void setFreq(unsigned freq); inline void keyOn(); inline void keyOff(); template void serialize(Archive& ar, unsigned version); Y8950Slot slot[2]; unsigned freq; // combined F-Number and Block bool alg; }; class Y8950::Impl : private ResampledSoundDevice, private EmuTimerCallback { public: Impl(Y8950& self, const std::string& name, const DeviceConfig& config, unsigned sampleRam, MSXAudio& audio); void init(const DeviceConfig& config, EmuTime::param time); virtual ~Impl(); void setEnabled(bool enabled, EmuTime::param time); void clearRam(); void reset(EmuTime::param time); void writeReg(byte reg, byte data, EmuTime::param time); byte readReg(byte reg, EmuTime::param time); byte peekReg(byte reg, EmuTime::param time) const; byte readStatus(EmuTime::param time); byte peekStatus(EmuTime::param time) const; void setStatus(byte flags); void resetStatus(byte flags); byte peekRawStatus() const; template void serialize(Archive& ar, unsigned version); private: // SoundDevice virtual int getAmplificationFactor() const; virtual void generateChannels(int** bufs, unsigned num); inline void keyOn_BD(); inline void keyOn_SD(); inline void keyOn_TOM(); inline void keyOn_HH(); inline void keyOn_CYM(); inline void keyOff_BD(); inline void keyOff_SD(); inline void keyOff_TOM(); inline void keyOff_HH(); inline void keyOff_CYM(); inline void setRythmMode(int data); bool checkMuteHelper(); void changeStatusMask(byte newMask); void callback(byte flag); MSXMotherBoard& motherBoard; Y8950Periphery& periphery; const std::unique_ptr adpcm; const std::unique_ptr connector; const std::unique_ptr dac13; // 13-bit (exponential) DAC const std::unique_ptr debuggable; const std::unique_ptr timer1; // 80us timer const std::unique_ptr timer2; // 320us timer IRQHelper irq; byte reg[0x100]; Y8950Channel ch[9]; unsigned pm_phase; // Pitch Modulator unsigned am_phase; // Amp Modulator // Noise Generator int noise_seed; unsigned noiseA_phase; unsigned noiseB_phase; unsigned noiseA_dphase; unsigned noiseB_dphase; byte status; // STATUS Register byte statusMask; // bit=0 -> masked bool rythm_mode; bool am_mode; bool pm_mode; bool enabled; }; static const double EG_STEP = 0.1875; // 3/16 static const double SL_STEP = 3.0; static const double TL_STEP = 0.75; // 12/16 static const double DB_STEP = 0.1875; // 3/16 static const unsigned SL_PER_EG = 16; // SL_STEP / EG_STEP static const unsigned TL_PER_EG = 4; // TL_STEP / EG_STEP static const unsigned EG_PER_DB = 1; // EG_STEP / DB_STEP // PM speed(Hz) and depth(cent) static const double PM_SPEED = 6.4; static const double PM_DEPTH = 13.75 / 2; static const double PM_DEPTH2 = 13.75; // Dynamic range of sustine level static const int SL_BITS = 4; static const int SL_MUTE = 1 << SL_BITS; // Size of Sintable ( 1 -- 18 can be used, but 7 -- 14 recommended.) static const int PG_BITS = 10; static const int PG_WIDTH = 1 << PG_BITS; static const int PG_MASK = PG_WIDTH - 1; // Phase increment counter static const int DP_BITS = 19; static const int DP_BASE_BITS = DP_BITS - PG_BITS; // WaveTable for each envelope amp. // values are in range[ 0, DB_MUTE) (for positive values) // or [2*DB_MUTE, 3*DB_MUTE) (for negative values) static unsigned sintable[PG_WIDTH]; // Phase incr table for Attack. static EnvPhaseIndex dphaseARTable[16][16]; // Phase incr table for Decay and Release. static EnvPhaseIndex dphaseDRTable[16][16]; // TL Table. static int tllTable[16 * 8][4]; // Liner to Log curve conversion table (for Attack rate). // values are in the range [0 .. EG_MUTE] static unsigned AR_ADJUST_TABLE[1 << EG_BITS]; // Definition of envelope mode enum { ATTACK, DECAY, SUSHOLD, SUSTINE, RELEASE, FINISH }; // Dynamic range static const int DB_BITS = 9; static const int DB_MUTE = 1 << DB_BITS; // PM table is calcurated by PM_AMP * pow(2, PM_DEPTH * sin(x) / 1200) static const int PM_AMP_BITS = 8; static const int PM_AMP = 1 << PM_AMP_BITS; // Bits for liner value static const int DB2LIN_AMP_BITS = 11; static const int SLOT_AMP_BITS = DB2LIN_AMP_BITS; // Bits for Pitch and Amp modulator static const int PM_PG_BITS = 8; static const int PM_PG_WIDTH = 1 << PM_PG_BITS; static const int PM_DP_BITS = 16; static const int PM_DP_WIDTH = 1 << PM_DP_BITS; static const int AM_PG_BITS = 8; static const int AM_PG_WIDTH = 1 << AM_PG_BITS; static const int AM_DP_BITS = 16; static const int AM_DP_WIDTH = 1 << AM_DP_BITS; // LFO Table static const unsigned PM_DPHASE = unsigned(PM_SPEED * PM_DP_WIDTH / (Y8950::CLOCK_FREQ / double(Y8950::CLOCK_FREQ_DIV))); static int pmtable[2][PM_PG_WIDTH]; // dB to Liner table static int dB2LinTab[(2 * DB_MUTE) * 2]; // LFO Amplitude Modulation table (verified on real YM3812) // 27 output levels (triangle waveform); // 1 level takes one of: 192, 256 or 448 samples // // Length: 210 elements. // Each of the elements has to be repeated // exactly 64 times (on 64 consecutive samples). // The whole table takes: 64 * 210 = 13440 samples. // // Verified on real YM3812 (OPL2), but I believe it's the same for Y8950 // because it closely matches the Y8950 AM parameters: // speed = 3.7Hz // depth = 4.875dB // Also this approch can be easily implemented in HW, the previous one (see SVN // history) could not. static const unsigned LFO_AM_TAB_ELEMENTS = 210; static const byte lfo_am_table[LFO_AM_TAB_ELEMENTS] = { 0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25, 24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1 }; //**************************************************// // // // Helper functions // // // //**************************************************// static inline unsigned DB_POS(int x) { int result = int(x / DB_STEP); assert(result < DB_MUTE); assert(result >= 0); return result; } static inline unsigned DB_NEG(int x) { return 2 * DB_MUTE + DB_POS(x); } //**************************************************// // // // Create tables // // // //**************************************************// // Table for AR to LogCurve. static void makeAdjustTable() { AR_ADJUST_TABLE[0] = EG_MUTE; for (int i = 1; i < (1 << EG_BITS); ++i) { AR_ADJUST_TABLE[i] = int(double(EG_MUTE) - 1 - EG_MUTE * ::log(double(i)) / ::log(double(1 << EG_BITS))) >> 1; assert(AR_ADJUST_TABLE[i] <= EG_MUTE); assert(int(AR_ADJUST_TABLE[i]) >= 0); } } // Table for dB(0 -- (1<= 0); assert(result <= DB_MUTE - 1); return result; } // Sin Table static void makeSinTable() { for (int i = 0; i < PG_WIDTH / 4; ++i) { sintable[i] = lin2db(sin(2.0 * M_PI * i / PG_WIDTH)); } for (int i = 0; i < PG_WIDTH / 4; i++) { sintable[PG_WIDTH / 2 - 1 - i] = sintable[i]; } for (int i = 0; i < PG_WIDTH / 2; i++) { sintable[PG_WIDTH / 2 + i] = 2 * DB_MUTE + sintable[i]; } } // Table for Pitch Modulator static void makePmTable() { for (int i = 0; i < PM_PG_WIDTH; ++i) { pmtable[0][i] = int(double(PM_AMP) * pow(2, double(PM_DEPTH) * sin(2.0 * M_PI * i / PM_PG_WIDTH) / 1200)); pmtable[1][i] = int(double(PM_AMP) * pow(2, double(PM_DEPTH2) * sin(2.0 * M_PI * i / PM_PG_WIDTH) / 1200)); } } static void makeTllTable() { // Processed version of Table 3.5 from the Application Manual static const unsigned kltable[16] = { 0, 24, 32, 37, 40, 43, 45, 47, 48, 50, 51, 52, 53, 54, 55, 56 }; // This is indeed {0.0, 3.0, 1.5, 6.0} dB/oct, verified on real Y8950. // Note the illogical order of 2nd and 3rd element. static const unsigned shift[4] = { 31, 1, 2, 0 }; for (unsigned freq = 0; freq < 16 * 8; ++freq) { unsigned fnum = freq % 16; unsigned block = freq / 16; int tmp = 4 * kltable[fnum] - 32 * (7 - block); for (unsigned KL = 0; KL < 4; ++KL) { tllTable[freq][KL] = (tmp <= 0) ? 0 : (tmp >> shift[KL]); } } } // Rate Table for Attack static void makeDphaseARTable() { for (unsigned Rks = 0; Rks < 16; ++Rks) { dphaseARTable[Rks][0] = EnvPhaseIndex(0); for (unsigned AR = 1; AR < 15; ++AR) { unsigned RM = std::min(AR + (Rks >> 2), 15u); unsigned RL = Rks & 3; dphaseARTable[Rks][AR] = EnvPhaseIndex(12 * (RL + 4)) >> (15 - RM); } dphaseARTable[Rks][15] = EG_DP_MAX; } } // Rate Table for Decay static void makeDphaseDRTable() { for (unsigned Rks = 0; Rks < 16; ++Rks) { dphaseDRTable[Rks][0] = EnvPhaseIndex(0); for (unsigned DR = 1; DR < 16; ++DR) { unsigned RM = std::min(DR + (Rks >> 2), 15u); unsigned RL = Rks & 3; dphaseDRTable[Rks][DR] = EnvPhaseIndex(RL + 4) >> (15 - RM); } } } // class Y8950Patch Y8950Patch::Y8950Patch() { reset(); } void Y8950Patch::reset() { AM = false; PM = false; EG = false; ML = 0; KL = 0; TL = 0; AR = 0; DR = 0; SL = 0; RR = 0; setKeyScaleRate(false); setFeedbackShift(0); } // class Y8950Slot void Y8950Slot::reset() { phase = 0; output = 0; feedback = 0; eg_mode = FINISH; eg_phase = EG_DP_MAX; slotStatus = false; patch.reset(); // this initializes: // dphase, tll, dphaseARTableRks, dphaseDRTableRks, eg_dphase updateAll(0); } void Y8950Slot::updatePG(unsigned freq) { static const int mltable[16] = { 1, 1*2, 2*2, 3*2, 4*2, 5*2, 6*2 , 7*2, 8*2, 9*2, 10*2, 10*2, 12*2, 12*2, 15*2, 15*2 }; unsigned fnum = freq % 1024; unsigned block = freq / 1024; dphase = ((fnum * mltable[patch.ML]) << block) >> (21 - DP_BITS); } void Y8950Slot::updateTLL(unsigned freq) { tll = tllTable[freq >> 6][patch.KL] + patch.TL * TL_PER_EG; } void Y8950Slot::updateRKS(unsigned freq) { unsigned rks = freq >> patch.KR; assert(rks < 16); dphaseARTableRks = dphaseARTable[rks]; dphaseDRTableRks = dphaseDRTable[rks]; } void Y8950Slot::updateEG() { switch (eg_mode) { case ATTACK: eg_dphase = dphaseARTableRks[patch.AR]; break; case DECAY: eg_dphase = dphaseDRTableRks[patch.DR]; break; case SUSTINE: eg_dphase = dphaseDRTableRks[patch.RR]; break; case RELEASE: eg_dphase = dphaseDRTableRks[patch.EG ? patch.RR : 7]; break; case SUSHOLD: case FINISH: eg_dphase = EnvPhaseIndex(0); break; } } void Y8950Slot::updateAll(unsigned freq) { updatePG(freq); updateTLL(freq); updateRKS(freq); updateEG(); // EG should be last } bool Y8950Slot::isActive() const { return eg_mode != FINISH; } // Slot key on void Y8950Slot::slotOn() { if (!slotStatus) { slotStatus = true; eg_mode = ATTACK; phase = 0; eg_phase = EnvPhaseIndex(0); } } // Slot key off void Y8950Slot::slotOff() { if (slotStatus) { slotStatus = false; if (eg_mode == ATTACK) { eg_phase = EnvPhaseIndex(AR_ADJUST_TABLE[eg_phase.toInt()]); } eg_mode = RELEASE; } } // class Y8950Channel Y8950Channel::Y8950Channel() { reset(); } void Y8950Channel::reset() { setFreq(0); slot[MOD].reset(); slot[CAR].reset(); alg = false; } // Set frequency (combined F-Number (10bit) and Block (3bit)) void Y8950Channel::setFreq(unsigned freq_) { freq = freq_; } void Y8950Channel::keyOn() { slot[MOD].slotOn(); slot[CAR].slotOn(); } void Y8950Channel::keyOff() { slot[MOD].slotOff(); slot[CAR].slotOff(); } Y8950::Impl::Impl(Y8950& self, const std::string& name, const DeviceConfig& config, unsigned sampleRam, MSXAudio& audio) : ResampledSoundDevice(config.getMotherBoard(), name, "MSX-AUDIO", 9 + 5 + 1) , motherBoard(config.getMotherBoard()) , periphery(audio.createPeriphery(getName())) , adpcm(make_unique( self, config, name, sampleRam)) , connector(make_unique( motherBoard.getPluggingController())) , dac13(make_unique( name + " DAC", "MSX-AUDIO 13-bit DAC", config)) , debuggable(make_unique( motherBoard, self, getName())) , timer1(EmuTimer::createOPL3_1(motherBoard.getScheduler(), *this)) , timer2(EmuTimer::createOPL3_2(motherBoard.getScheduler(), *this)) , irq(motherBoard, getName() + ".IRQ") , enabled(true) { } // Constructor is split in two phases (actual constructor and this init() // method). Reason is that adpcm->reset() calls setStatus() via the Y8950 // object, but before constructor is finished the pointer from Y8950 to // Y8950Impl is not yet initialized. void Y8950::Impl::init(const DeviceConfig& config, EmuTime::param time) { makePmTable(); makeAdjustTable(); makeDB2LinTable(); makeTllTable(); makeSinTable(); makeDphaseARTable(); makeDphaseDRTable(); double input = Y8950::CLOCK_FREQ / double(Y8950::CLOCK_FREQ_DIV); setInputRate(int(input + 0.5)); reset(time); registerSound(config); } Y8950::Impl::~Impl() { unregisterSound(); } void Y8950::Impl::clearRam() { adpcm->clearRam(); } // Reset whole of opl except patch datas. void Y8950::Impl::reset(EmuTime::param time) { for (int i = 0; i < 9; ++i) { ch[i].reset(); } rythm_mode = false; am_mode = false; pm_mode = false; pm_phase = 0; am_phase = 0; noise_seed = 0xffff; noiseA_phase = 0; noiseB_phase = 0; noiseA_dphase = 0; noiseB_dphase = 0; // update the output buffer before changing the register updateStream(time); for (int i = 0; i < 0x100; ++i) { reg[i] = 0x00; } reg[0x04] = 0x18; reg[0x19] = 0x0F; // fixes 'Thunderbirds are Go' status = 0x00; statusMask = 0; irq.reset(); adpcm->reset(time); } // Drum key on void Y8950::Impl::keyOn_BD() { ch[6].keyOn(); } void Y8950::Impl::keyOn_HH() { ch[7].slot[MOD].slotOn(); } void Y8950::Impl::keyOn_SD() { ch[7].slot[CAR].slotOn(); } void Y8950::Impl::keyOn_TOM() { ch[8].slot[MOD].slotOn(); } void Y8950::Impl::keyOn_CYM() { ch[8].slot[CAR].slotOn(); } // Drum key off void Y8950::Impl::keyOff_BD() { ch[6].keyOff(); } void Y8950::Impl::keyOff_HH() { ch[7].slot[MOD].slotOff(); } void Y8950::Impl::keyOff_SD() { ch[7].slot[CAR].slotOff(); } void Y8950::Impl::keyOff_TOM(){ ch[8].slot[MOD].slotOff(); } void Y8950::Impl::keyOff_CYM(){ ch[8].slot[CAR].slotOff(); } // Change Rhythm Mode void Y8950::Impl::setRythmMode(int data) { bool newMode = (data & 32) != 0; if (rythm_mode != newMode) { rythm_mode = newMode; if (!rythm_mode) { // ON->OFF ch[6].slot[MOD].eg_mode = FINISH; // BD1 ch[6].slot[MOD].slotStatus = false; ch[6].slot[CAR].eg_mode = FINISH; // BD2 ch[6].slot[CAR].slotStatus = false; ch[7].slot[MOD].eg_mode = FINISH; // HH ch[7].slot[MOD].slotStatus = false; ch[7].slot[CAR].eg_mode = FINISH; // SD ch[7].slot[CAR].slotStatus = false; ch[8].slot[MOD].eg_mode = FINISH; // TOM ch[8].slot[MOD].slotStatus = false; ch[8].slot[CAR].eg_mode = FINISH; // CYM ch[8].slot[CAR].slotStatus = false; } } } // // Generate wave data // // Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI). static inline int wave2_8pi(int e) { int shift = SLOT_AMP_BITS - PG_BITS - 2; return (shift > 0) ? (e >> shift) : (e << -shift); } unsigned Y8950Slot::calc_phase(int lfo_pm) { if (patch.PM) { phase += (dphase * lfo_pm) >> PM_AMP_BITS; } else { phase += dphase; } return phase >> DP_BASE_BITS; } #define S2E(x) EnvPhaseIndex(int(x / EG_STEP)) static const EnvPhaseIndex SL[16] = { S2E( 0), S2E( 3), S2E( 6), S2E( 9), S2E(12), S2E(15), S2E(18), S2E(21), S2E(24), S2E(27), S2E(30), S2E(33), S2E(36), S2E(39), S2E(42), S2E(93) }; unsigned Y8950Slot::calc_envelope(int lfo_am) { unsigned egout = 0; switch (eg_mode) { case ATTACK: eg_phase += eg_dphase; if (eg_phase >= EG_DP_MAX) { egout = 0; eg_phase = EnvPhaseIndex(0); eg_mode = DECAY; updateEG(); } else { egout = AR_ADJUST_TABLE[eg_phase.toInt()]; } break; case DECAY: eg_phase += eg_dphase; if (eg_phase >= SL[patch.SL]) { eg_phase = SL[patch.SL]; eg_mode = patch.EG ? SUSHOLD : SUSTINE; updateEG(); } egout = eg_phase.toInt(); break; case SUSHOLD: egout = eg_phase.toInt(); if (!patch.EG) { eg_mode = SUSTINE; updateEG(); } break; case SUSTINE: case RELEASE: eg_phase += eg_dphase; egout = eg_phase.toInt(); if (egout >= EG_MUTE) { eg_mode = FINISH; egout = EG_MUTE - 1; } break; case FINISH: egout = EG_MUTE - 1; break; } egout = ((egout + tll) * EG_PER_DB); if (patch.AM) { egout += lfo_am; } return std::min(egout, DB_MUTE - 1); } int Y8950Slot::calc_slot_car(int lfo_pm, int lfo_am, int fm) { unsigned egout = calc_envelope(lfo_am); int pgout = calc_phase(lfo_pm) + wave2_8pi(fm); return dB2LinTab[sintable[pgout & PG_MASK] + egout]; } int Y8950Slot::calc_slot_mod(int lfo_pm, int lfo_am) { unsigned egout = calc_envelope(lfo_am); unsigned pgout = calc_phase(lfo_pm); if (patch.FB != 0) { pgout += wave2_8pi(feedback) >> patch.FB; } int newOutput = dB2LinTab[sintable[pgout & PG_MASK] + egout]; feedback = (output + newOutput) >> 1; output = newOutput; return feedback; } int Y8950Slot::calc_slot_tom(int lfo_pm, int lfo_am) { unsigned egout = calc_envelope(lfo_am); unsigned pgout = calc_phase(lfo_pm); return dB2LinTab[sintable[pgout & PG_MASK] + egout]; } int Y8950Slot::calc_slot_snare(int lfo_pm, int lfo_am, int whitenoise) { unsigned egout = calc_envelope(lfo_am); unsigned pgout = calc_phase(lfo_pm); unsigned tmp = (pgout & (1 << (PG_BITS - 1))) ? 0 : 2 * DB_MUTE; return (dB2LinTab[tmp + egout] + dB2LinTab[egout + whitenoise]) >> 1; } int Y8950Slot::calc_slot_cym(int lfo_am, int a, int b) { unsigned egout = calc_envelope(lfo_am); return (dB2LinTab[egout + a] + dB2LinTab[egout + b]) >> 1; } // HI-HAT int Y8950Slot::calc_slot_hat(int lfo_am, int a, int b, int whitenoise) { unsigned egout = calc_envelope(lfo_am); return (dB2LinTab[egout + whitenoise] + dB2LinTab[egout + a] + dB2LinTab[egout + b]) >> 2; } int Y8950::Impl::getAmplificationFactor() const { return 1 << (15 - DB2LIN_AMP_BITS); } void Y8950::Impl::setEnabled(bool enabled_, EmuTime::param time) { updateStream(time); enabled = enabled_; } bool Y8950::Impl::checkMuteHelper() { if (!enabled) { return true; } for (int i = 0; i < 6; ++i) { if (ch[i].slot[CAR].isActive()) return false; } if (!rythm_mode) { for(int i = 6; i < 9; ++i) { if (ch[i].slot[CAR].isActive()) return false; } } else { if (ch[6].slot[CAR].isActive()) return false; if (ch[7].slot[MOD].isActive()) return false; if (ch[7].slot[CAR].isActive()) return false; if (ch[8].slot[MOD].isActive()) return false; if (ch[8].slot[CAR].isActive()) return false; } return adpcm->isMuted(); } void Y8950::Impl::generateChannels(int** bufs, unsigned num) { // TODO implement per-channel mute (instead of all-or-nothing) if (checkMuteHelper()) { // TODO update internal state even when muted // during mute pm_phase, am_phase, noiseA_phase, noiseB_phase // and noise_seed aren't updated, probably ok for (int i = 0; i < 9 + 5 + 1; ++i) { bufs[i] = nullptr; } return; } for (unsigned sample = 0; sample < num; ++sample) { // Amplitude modulation: 27 output levels (triangle waveform); // 1 level takes one of: 192, 256 or 448 samples // One entry from LFO_AM_TABLE lasts for 64 samples // lfo_am_table is 210 elements long ++am_phase; if (am_phase == (LFO_AM_TAB_ELEMENTS * 64)) am_phase = 0; unsigned tmp = lfo_am_table[am_phase / 64]; int lfo_am = am_mode ? tmp : tmp / 4; pm_phase = (pm_phase + PM_DPHASE) & (PM_DP_WIDTH - 1); int lfo_pm = pmtable[pm_mode][pm_phase >> (PM_DP_BITS - PM_PG_BITS)]; if (noise_seed & 1) { noise_seed ^= 0x24000; } noise_seed >>= 1; int whitenoise = noise_seed & 1 ? DB_POS(6) : DB_NEG(6); noiseA_phase += noiseA_dphase; noiseA_phase &= (0x40 << 11) - 1; if ((noiseA_phase >> 11) == 0x3f) { noiseA_phase = 0; } int noiseA = noiseA_phase & (0x03 << 11) ? DB_POS(6) : DB_NEG(6); noiseB_phase += noiseB_dphase; noiseB_phase &= (0x10 << 11) - 1; int noiseB = noiseB_phase & (0x0A << 11) ? DB_POS(6) : DB_NEG(6); int m = rythm_mode ? 6 : 9; for (int i = 0; i < m; ++i) { if (ch[i].slot[CAR].isActive()) { bufs[i][sample] += ch[i].alg ? ch[i].slot[CAR].calc_slot_car(lfo_pm, lfo_am, 0) + ch[i].slot[MOD].calc_slot_mod(lfo_pm, lfo_am) : ch[i].slot[CAR].calc_slot_car(lfo_pm, lfo_am, ch[i].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)); } else { //bufs[i][sample] += 0; } } if (rythm_mode) { //bufs[6][sample] += 0; //bufs[7][sample] += 0; //bufs[8][sample] += 0; // TODO wasn't in original source either ch[7].slot[MOD].calc_phase(lfo_pm); ch[8].slot[CAR].calc_phase(lfo_pm); bufs[ 9][sample] += (ch[6].slot[CAR].isActive()) ? 2 * ch[6].slot[CAR].calc_slot_car(lfo_pm, lfo_am, ch[6].slot[MOD].calc_slot_mod(lfo_pm, lfo_am)) : 0; bufs[10][sample] += (ch[7].slot[CAR].isActive()) ? 2 * ch[7].slot[CAR].calc_slot_snare(lfo_pm, lfo_am, whitenoise) : 0; bufs[11][sample] += (ch[8].slot[CAR].isActive()) ? 2 * ch[8].slot[CAR].calc_slot_cym(lfo_am, noiseA, noiseB) : 0; bufs[12][sample] += (ch[7].slot[MOD].isActive()) ? 2 * ch[7].slot[MOD].calc_slot_hat(lfo_am, noiseA, noiseB, whitenoise) : 0; bufs[13][sample] += (ch[8].slot[MOD].isActive()) ? 2 * ch[8].slot[MOD].calc_slot_tom(lfo_pm, lfo_am) : 0; } else { //bufs[ 9] += 0; //bufs[10] += 0; //bufs[11] += 0; //bufs[12] += 0; //bufs[13] += 0; } bufs[14][sample] += adpcm->calcSample(); } } // // I/O Ctrl // void Y8950::Impl::writeReg(byte rg, byte data, EmuTime::param time) { int stbl[32] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; // TODO only for registers that influence sound // TODO also ADPCM //if (rg >= 0x20) { // update the output buffer before changing the register updateStream(time); //} switch (rg & 0xe0) { case 0x00: { switch (rg) { case 0x01: // TEST // TODO // Y8950 MSX-AUDIO Test register $01 (write only) // // Bit Description // // 7 Reset LFOs - seems to force the LFOs to their initial // values (eg. maximum amplitude, zero phase deviation) // // 6 something to do with ADPCM - bit 0 of the status // register is affected by setting this bit (PCM BSY) // // 5 No effect? - Waveform select enable in YM3812 OPL2 so seems // reasonable that this bit wouldn't have been used in OPL // // 4 No effect? // // 3 Faster LFOs - increases the frequencies of the LFOs and // (maybe) the timers (cf. YM2151 test register) // // 2 Reset phase generators - No phase generator output, but // envelope generators still work (can hear a transient // when they are gated) // // 1 No effect? // // 0 Reset envelopes - Envelope generator outputs forced // to maximum, so all enabled voices sound at maximum reg[rg] = data; break; case 0x02: // TIMER1 (reso. 80us) timer1->setValue(data); reg[rg] = data; break; case 0x03: // TIMER2 (reso. 320us) timer2->setValue(data); reg[rg] = data; break; case 0x04: // FLAG CONTROL if (data & Y8950::R04_IRQ_RESET) { resetStatus(0x78); // reset all flags } else { changeStatusMask((~data) & 0x78); timer1->setStart((data & Y8950::R04_ST1) != 0, time); timer2->setStart((data & Y8950::R04_ST2) != 0, time); reg[rg] = data; } adpcm->resetStatus(); break; case 0x06: // (KEYBOARD OUT) connector->write(data, time); reg[rg] = data; break; case 0x07: // START/REC/MEM DATA/REPEAT/SP-OFF/-/-/RESET periphery.setSPOFF((data & 8) != 0, time); // bit 3 // fall-through case 0x08: // CSM/KEY BOARD SPLIT/-/-/SAMPLE/DA AD/64K/ROM case 0x09: // START ADDRESS (L) case 0x0A: // START ADDRESS (H) case 0x0B: // STOP ADDRESS (L) case 0x0C: // STOP ADDRESS (H) case 0x0D: // PRESCALE (L) case 0x0E: // PRESCALE (H) case 0x0F: // ADPCM-DATA case 0x10: // DELTA-N (L) case 0x11: // DELTA-N (H) case 0x12: // ENVELOP CONTROL case 0x1A: // PCM-DATA reg[rg] = data; adpcm->writeReg(rg, data, time); break; case 0x15: // DAC-DATA (bit9-2) reg[rg] = data; if (reg[0x08] & 0x04) { int tmp = static_cast(reg[0x15]) * 256 + reg[0x16]; tmp = (tmp * 4) >> (7 - reg[0x17]); tmp = Math::clipIntToShort(tmp); dac13->writeDAC(tmp, time); } break; case 0x16: // (bit1-0) reg[rg] = data & 0xC0; break; case 0x17: // (exponent) reg[rg] = data & 0x07; break; case 0x18: // I/O-CONTROL (bit3-0) // 0 -> input // 1 -> output reg[rg] = data; periphery.write(reg[0x18], reg[0x19], time); break; case 0x19: // I/O-DATA (bit3-0) reg[rg] = data; periphery.write(reg[0x18], reg[0x19], time); break; } break; } case 0x20: { int s = stbl[rg & 0x1f]; if (s >= 0) { Y8950Channel& chan = ch[s / 2]; Y8950Slot& slot = chan.slot[s & 1]; slot.patch.AM = (data >> 7) & 1; slot.patch.PM = (data >> 6) & 1; slot.patch.EG = (data >> 5) & 1; slot.patch.setKeyScaleRate((data & 0x10) != 0); slot.patch.ML = (data >> 0) & 15; slot.updateAll(chan.freq); } reg[rg] = data; break; } case 0x40: { int s = stbl[rg & 0x1f]; if (s >= 0) { Y8950Channel& chan = ch[s / 2]; Y8950Slot& slot = chan.slot[s & 1]; slot.patch.KL = (data >> 6) & 3; slot.patch.TL = (data >> 0) & 63; slot.updateAll(chan.freq); } reg[rg] = data; break; } case 0x60: { int s = stbl[rg & 0x1f]; if (s >= 0) { Y8950Slot& slot = ch[s / 2].slot[s & 1]; slot.patch.AR = (data >> 4) & 15; slot.patch.DR = (data >> 0) & 15; slot.updateEG(); } reg[rg] = data; break; } case 0x80: { int s = stbl[rg & 0x1f]; if (s >= 0) { Y8950Slot& slot = ch[s / 2].slot[s & 1]; slot.patch.SL = (data >> 4) & 15; slot.patch.RR = (data >> 0) & 15; slot.updateEG(); } reg[rg] = data; break; } case 0xa0: { if (rg == 0xbd) { am_mode = (data & 0x80) != 0; pm_mode = (data & 0x40) != 0; setRythmMode(data); if (rythm_mode) { if (data & 0x10) keyOn_BD(); else keyOff_BD(); if (data & 0x08) keyOn_SD(); else keyOff_SD(); if (data & 0x04) keyOn_TOM(); else keyOff_TOM(); if (data & 0x02) keyOn_CYM(); else keyOff_CYM(); if (data & 0x01) keyOn_HH(); else keyOff_HH(); } ch[6].slot[MOD].updateAll(ch[6].freq); ch[6].slot[CAR].updateAll(ch[6].freq); ch[7].slot[MOD].updateAll(ch[7].freq); ch[7].slot[CAR].updateAll(ch[7].freq); ch[8].slot[MOD].updateAll(ch[8].freq); ch[8].slot[CAR].updateAll(ch[8].freq); reg[rg] = data; break; } unsigned c = rg & 0x0f; if (c > 8) { // 0xa9-0xaf 0xb9-0xbf break; } unsigned freq; if (!(rg & 0x10)) { // 0xa0-0xa8 freq = data | ((reg[rg + 0x10] & 0x1F) << 8); } else { // 0xb0-0xb8 if (data & 0x20) { ch[c].keyOn(); } else { ch[c].keyOff(); } freq = reg[rg - 0x10] | ((data & 0x1F) << 8); } ch[c].setFreq(freq); unsigned fNum = freq % 1024; unsigned block = freq / 1024; switch (c) { case 7: noiseA_dphase = fNum << block; break; case 8: noiseB_dphase = fNum << block; break; } ch[c].slot[CAR].updateAll(freq); ch[c].slot[MOD].updateAll(freq); reg[rg] = data; break; } case 0xc0: { if (rg > 0xc8) break; int c = rg - 0xC0; ch[c].slot[MOD].patch.setFeedbackShift((data >> 1) & 7); ch[c].alg = data & 1; reg[rg] = data; } } } byte Y8950::Impl::readReg(byte rg, EmuTime::param time) { updateStream(time); // TODO only when necessary byte result; switch (rg) { case 0x0F: // ADPCM-DATA case 0x13: // ??? case 0x14: // ??? case 0x1A: // PCM-DATA result = adpcm->readReg(rg, time); break; default: result = peekReg(rg, time); } return result; } byte Y8950::Impl::peekReg(byte rg, EmuTime::param time) const { switch (rg) { case 0x05: // (KEYBOARD IN) return connector->read(time); // TODO peek iso read case 0x0F: // ADPCM-DATA case 0x13: // ??? case 0x14: // ??? case 0x1A: // PCM-DATA return adpcm->peekReg(rg, time); case 0x19: { // I/O DATA byte input = periphery.read(time); byte output = reg[0x19]; byte enable = reg[0x18]; return (output & enable) | (input & ~enable) | 0xF0; } default: return reg[rg]; } } byte Y8950::Impl::readStatus(EmuTime::param time) { byte result = peekStatus(time); //std::cout << "status: " << (int)result << std::endl; return result; } byte Y8950::Impl::peekStatus(EmuTime::param time) const { adpcm->sync(time); return (status & (0x80 | statusMask)) | 0x06; // bit 1 and 2 are always 1 } void Y8950::Impl::callback(byte flag) { setStatus(flag); } void Y8950::Impl::setStatus(byte flags) { status |= flags; if (status & statusMask) { status |= 0x80; irq.set(); } } void Y8950::Impl::resetStatus(byte flags) { status &= ~flags; if (!(status & statusMask)) { status &= 0x7f; irq.reset(); } } byte Y8950::Impl::peekRawStatus() const { return status; } void Y8950::Impl::changeStatusMask(byte newMask) { statusMask = newMask; status &= statusMask; if (status) { status |= 0x80; irq.set(); } else { status &= 0x7f; irq.reset(); } } template void Y8950Patch::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("AM", AM); ar.serialize("PM", PM); ar.serialize("EG", EG); ar.serialize("KR", KR); ar.serialize("ML", ML); ar.serialize("KL", KL); ar.serialize("TL", TL); ar.serialize("FB", FB); ar.serialize("AR", AR); ar.serialize("DR", DR); ar.serialize("SL", SL); ar.serialize("RR", RR); } template void Y8950Slot::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("feedback", feedback); ar.serialize("output", output); ar.serialize("phase", phase); ar.serialize("eg_mode", eg_mode); ar.serialize("eg_phase", eg_phase); ar.serialize("patch", patch); ar.serialize("slotStatus", slotStatus); // These are restored by call to updateAll() in Y8950Channel::serialize() // dphase, tll, dphaseARTableRks, dphaseDRTableRks, eg_dphase } template void Y8950Channel::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("mod", slot[MOD]); ar.serialize("car", slot[CAR]); ar.serialize("freq", freq); ar.serialize("alg", alg); if (ar.isLoader()) { slot[MOD].updateAll(freq); slot[CAR].updateAll(freq); } } template void Y8950::Impl::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("keyboardConnector", *connector); ar.serialize("adpcm", *adpcm); ar.serialize("timer1", *timer1); ar.serialize("timer2", *timer2); ar.serialize("irq", irq); ar.serialize_blob("registers", reg, sizeof(reg)); ar.serialize("pm_phase", pm_phase); ar.serialize("am_phase", am_phase); ar.serialize("noise_seed", noise_seed); ar.serialize("noiseA_phase", noiseA_phase); ar.serialize("noiseB_phase", noiseB_phase); ar.serialize("noiseA_dphase", noiseA_dphase); ar.serialize("noiseB_dphase", noiseB_dphase); ar.serialize("channels", ch); ar.serialize("status", status); ar.serialize("statusMask", statusMask); ar.serialize("rythm_mode", rythm_mode); ar.serialize("am_mode", am_mode); ar.serialize("pm_mode", pm_mode); ar.serialize("enabled", enabled); // TODO restore more state from registers static const byte rewriteRegs[] = { 6, // connector 15, // dac13 }; if (ar.isLoader()) { EmuTime::param time = motherBoard.getCurrentTime(); for (unsigned i = 0; i < sizeof(rewriteRegs); ++i) { byte r = rewriteRegs[i]; writeReg(r, reg[r], time); } } } // SimpleDebuggable Y8950Debuggable::Y8950Debuggable(MSXMotherBoard& motherBoard, Y8950& y8950_, const std::string& name) : SimpleDebuggable(motherBoard, name + " regs", "MSX-AUDIO", 0x100) , y8950(y8950_) { } byte Y8950Debuggable::read(unsigned address, EmuTime::param time) { return y8950.peekReg(address, time); } void Y8950Debuggable::write(unsigned address, byte value, EmuTime::param time) { y8950.writeReg(address, value, time); } // class Y8950 Y8950::Y8950(const std::string& name, const DeviceConfig& config, unsigned sampleRam, EmuTime::param time, MSXAudio& audio) : pimpl(make_unique(*this, name, config, sampleRam, audio)) { pimpl->init(config, time); } Y8950::~Y8950() { } void Y8950::setEnabled(bool enabled, EmuTime::param time) { pimpl->setEnabled(enabled, time); } void Y8950::clearRam() { pimpl->clearRam(); } void Y8950::reset(EmuTime::param time) { pimpl->reset(time); } void Y8950::writeReg(byte reg, byte data, EmuTime::param time) { pimpl->writeReg(reg, data, time); } byte Y8950::readReg(byte reg, EmuTime::param time) { return pimpl->readReg(reg, time); } byte Y8950::peekReg(byte reg, EmuTime::param time) const { return pimpl->peekReg(reg, time); } byte Y8950::readStatus(EmuTime::param time) { return pimpl->readStatus(time); } byte Y8950::peekStatus(EmuTime::param time) const { return pimpl->peekStatus(time); } void Y8950::setStatus(byte flags) { pimpl->setStatus(flags); } void Y8950::resetStatus(byte flags) { pimpl->resetStatus(flags); } byte Y8950::peekRawStatus() const { return pimpl->peekRawStatus(); } template void Y8950::serialize(Archive& ar, unsigned version) { pimpl->serialize(ar, version); } INSTANTIATE_SERIALIZE_METHODS(Y8950); } // namespace openmsx openmsx-0.10.0/src/sound/VLM5030.cc0000644000175000017500000004266412262345041017254 0ustar manuelmanuel00000000000000/* vlm5030.c VLM5030 emulator Written by Tatsuyuki Satoh Based on TMS5220 simulator (tms5220.c) note: memory read cycle(==sampling rate) = 122.9u(440clock) interpolator (LC8109 = 2.5ms) = 20 * samples(125us) frame time (20ms) = 4 * interpolator 9bit DAC is composed of 5bit Physical and 3bitPWM. todo: Noise Generator circuit without 'rand()' function. ----------- command format (Analytical result) ---------- 1)end of speech (8bit) :00000011: 2)silent some frame (8bit) :????SS01: SS : number of silent frames 00 = 2 frame 01 = 4 frame 10 = 6 frame 11 = 8 frame 3)-speech frame (48bit) function: 6th : 5th : 4th : 3rd : 2nd : 1st : end : --- : --- : --- : --- : --- :00000011: silent : --- : --- : --- : --- : --- :0000SS01: speech :11111122:22233334:44455566:67778889:99AAAEEE:EEPPPPP0: EEEEE : energy : volume 0=off,0x1f=max PPPPP : pitch : 0=noize , 1=fast,0x1f=slow 111111 : K1 : 48=off 22222 : K2 : 0=off,1=+min,0x0f=+max,0x10=off,0x11=+max,0x1f=-min : 16 == special function?? 3333 : K3 : 0=off,1=+min,0x07=+max,0x08=-max,0x0f=-min 4444 : K4 : 555 : K5 : 0=off,1=+min,0x03=+max,0x04=-max,0x07=-min 666 : K6 : 777 : K7 : 888 : K8 : 999 : K9 : AAA : K10 : ---------- chirp table information ---------- DAC PWM cycle == 88system clock , (11clock x 8 pattern) = 40.6KHz one chirp == 5 x PWM cycle == 440systemclock(8,136Hz) chirp 0 : volume 10- 8 : with filter chirp 1 : volume 8- 6 : with filter chirp 2 : volume 6- 4 : with filter chirp 3 : volume 4 : no filter ?? chirp 4- 5: volume 4- 2 : with filter chirp 6-11: volume 2- 0 : with filter chirp 12-..: vokume 0 : silent ---------- digial output information ---------- when ME pin = high , some status output to A0..15 pins A0..8 : DAC output value (abs) A9 : DAC sign flag , L=minus,H=Plus A10 : energy reload flag (pitch pulse) A11..15 : unknown [DAC output value(signed 6bit)] = A9 ? A0..8 : -(A0..8) */ #include "VLM5030.hh" #include "ResampledSoundDevice.hh" #include "Rom.hh" #include "DeviceConfig.hh" #include "XMLElement.hh" #include "FileContext.hh" #include "FileOperations.hh" #include "serialize.hh" #include "memory.hh" #include #include namespace openmsx { class VLM5030::Impl : public ResampledSoundDevice { public: Impl(const std::string& name, const std::string& desc, const std::string& romFilename, const DeviceConfig& config); ~Impl(); void reset(); void writeData(byte data); void writeControl(byte data, EmuTime::param time); bool getBSY(EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: void setRST(bool pin); void setVCU(bool pin); void setST (bool pin); // SoundDevice virtual void generateChannels(int** bufs, unsigned num); void setupParameter(byte param); int getBits(unsigned sbit, unsigned bits); int parseFrame(); std::unique_ptr rom; int address_mask; // state of option paramter int frame_size; int pitch_offset; // these contain data describing the current and previous voice frames // these are all used to contain the current state of the sound generation unsigned current_energy; unsigned current_pitch; int current_k[10]; int x[10]; word address; word vcu_addr_h; signed_word old_k[10]; signed_word new_k[10]; signed_word target_k[10]; word old_energy; word new_energy; word target_energy; byte old_pitch; byte new_pitch; byte target_pitch; byte interp_step; byte interp_count; // number of interp periods byte sample_count; // sample number within interp byte pitch_count; byte latch_data; byte parameter; byte phase; bool pin_BSY; bool pin_ST; bool pin_VCU; bool pin_RST; }; // interpolator per frame static const int FR_SIZE = 4; // samples per interpolator static const int IP_SIZE_SLOWER = 240 / FR_SIZE; static const int IP_SIZE_SLOW = 200 / FR_SIZE; static const int IP_SIZE_NORMAL = 160 / FR_SIZE; static const int IP_SIZE_FAST = 120 / FR_SIZE; static const int IP_SIZE_FASTER = 80 / FR_SIZE; // phase value enum { PH_RESET, PH_IDLE, PH_SETUP, PH_WAIT, PH_RUN, PH_STOP, PH_END }; // speed parameter // SPC SPB SPA // 1 0 1 more slow (05h) : 42ms (150%) : 60sample // 1 1 x slow (06h,07h) : 34ms (125%) : 50sample // x 0 0 normal (00h,04h) : 25.6ms (100%) : 40samplme // 0 0 1 fast (01h) : 20.2ms (75%) : 30sample // 0 1 x more fast (02h,03h) : 12.2ms (50%) : 20sample static const int VLM5030_speed_table[8] = { IP_SIZE_NORMAL, IP_SIZE_FAST, IP_SIZE_FASTER, IP_SIZE_FASTER, IP_SIZE_NORMAL, IP_SIZE_SLOWER, IP_SIZE_SLOW, IP_SIZE_SLOW }; // ROM Tables // This is the energy lookup table // sampled from real chip static word energytable[0x20] = { 0, 2, 4, 6, 10, 12, 14, 18, // 0-7 22, 26, 30, 34, 38, 44, 48, 54, // 8-15 62, 68, 76, 84, 94,102,114,124, // 16-23 136,150,164,178,196,214,232,254 // 24-31 }; // This is the pitch lookup table static const byte pitchtable [0x20] = { 1, // 0 : random mode 22, // 1 : start=22 23, 24, 25, 26, 27, 28, 29, 30, // 2- 9 : 1step 32, 34, 36, 38, 40, 42, 44, 46, // 10-17 : 2step 50, 54, 58, 62, 66, 70, 74, 78, // 18-25 : 4step 86, 94, 102,110,118,126 // 26-31 : 8step }; static const signed_word K1_table[] = { -24898, -25672, -26446, -27091, -27736, -28252, -28768, -29155, -29542, -29929, -30316, -30574, -30832, -30961, -31219, -31348, -31606, -31735, -31864, -31864, -31993, -32122, -32122, -32251, -32251, -32380, -32380, -32380, -32509, -32509, -32509, -32509, 24898, 23995, 22963, 21931, 20770, 19480, 18061, 16642, 15093, 13416, 11610, 9804, 7998, 6063, 3999, 1935, 0, -1935, -3999, -6063, -7998, -9804, -11610, -13416, -15093, -16642, -18061, -19480, -20770, -21931, -22963, -23995 }; static const signed_word K2_table[] = { 0, -3096, -6321, -9417, -12513, -15351, -18061, -20770, -23092, -25285, -27220, -28897, -30187, -31348, -32122, -32638, 0, 32638, 32122, 31348, 30187, 28897, 27220, 25285, 23092, 20770, 18061, 15351, 12513, 9417, 6321, 3096 }; static const signed_word K3_table[] = { 0, -3999, -8127, -12255, -16384, -20383, -24511, -28639, 32638, 28639, 24511, 20383, 16254, 12255, 8127, 3999 }; static const signed_word K5_table[] = { 0, -8127, -16384, -24511, 32638, 24511, 16254, 8127 }; int VLM5030::Impl::getBits(unsigned sbit, unsigned bits) { unsigned offset = address + (sbit / 8); unsigned data = (*rom)[(offset + 0) & address_mask] + (*rom)[(offset + 1) & address_mask] * 256; data >>= (sbit & 7); data &= (0xFF >> (8 - bits)); return data; } // get next frame int VLM5030::Impl::parseFrame() { // remember previous frame old_energy = new_energy; old_pitch = new_pitch; for (int i = 0; i <= 9; ++i) { old_k[i] = new_k[i]; } // command byte check byte cmd = (*rom)[address & address_mask]; if (cmd & 0x01) { // extend frame new_energy = new_pitch = 0; for (int i = 0; i <= 9; ++i) { new_k[i] = 0; } ++address; if (cmd & 0x02) { // end of speech return 0; } else { // silent frame int nums = ((cmd >> 2) + 1) * 2; return nums * FR_SIZE; } } // pitch new_pitch = (pitchtable[getBits(1, 5)] + pitch_offset) & 0xff; // energy new_energy = energytable[getBits(6, 5)]; // 10 K's new_k[9] = K5_table[getBits(11, 3)]; new_k[8] = K5_table[getBits(14, 3)]; new_k[7] = K5_table[getBits(17, 3)]; new_k[6] = K5_table[getBits(20, 3)]; new_k[5] = K5_table[getBits(23, 3)]; new_k[4] = K5_table[getBits(26, 3)]; new_k[3] = K3_table[getBits(29, 4)]; new_k[2] = K3_table[getBits(33, 4)]; new_k[1] = K2_table[getBits(37, 5)]; new_k[0] = K1_table[getBits(42, 6)]; address += 6; return FR_SIZE; } // decode and buffering data void VLM5030::Impl::generateChannels(int** bufs, unsigned length) { if (phase == PH_IDLE) { bufs[0] = nullptr; return; } int buf_count = 0; // running if (phase == PH_RUN || phase == PH_STOP) { // playing speech while (length > 0) { int current_val; // check new interpolator or new frame if (sample_count == 0) { if (phase == PH_STOP) { phase = PH_END; sample_count = 1; goto phase_stop; // continue to end phase } sample_count = frame_size; // interpolator changes if (interp_count == 0) { // change to new frame interp_count = parseFrame(); // with change phase if (interp_count == 0 ) { // end mark found interp_count = FR_SIZE; sample_count = frame_size; // end -> stop time phase = PH_STOP; } // Set old target as new start of frame current_energy = old_energy; current_pitch = old_pitch; for (int i = 0; i <= 9; ++i) { current_k[i] = old_k[i]; } // is this a zero energy frame? if (current_energy == 0) { target_energy = 0; target_pitch = current_pitch; for (int i = 0; i <= 9; ++i) { target_k[i] = current_k[i]; } } else { // normal frame target_energy = new_energy; target_pitch = new_pitch; for (int i = 0; i <= 9; ++i) { target_k[i] = new_k[i]; } } } // next interpolator // Update values based on step values 25%, 50%, 75%, 100% interp_count -= interp_step; // 3,2,1,0 -> 1,2,3,4 int interp_effect = FR_SIZE - (interp_count % FR_SIZE); current_energy = old_energy + (target_energy - old_energy) * interp_effect / FR_SIZE; if (old_pitch > 1) { current_pitch = old_pitch + (target_pitch - old_pitch) * interp_effect / FR_SIZE; } for (int i = 0; i <= 9; ++i) current_k[i] = old_k[i] + (target_k[i] - old_k[i]) * interp_effect / FR_SIZE; } // calcrate digital filter if (old_energy == 0) { // generate silent samples here current_val = 0x00; } else if (old_pitch <= 1) { // generate unvoiced samples here current_val = (rand() & 1) ? int(current_energy) : -int(current_energy); } else { // generate voiced samples here current_val = (pitch_count == 0) ? current_energy : 0; } // Lattice filter here int u[11]; u[10] = current_val; for (int i = 9; i >= 0; --i) { u[i] = u[i + 1] - ((current_k[i] * x[i]) / 32768); } for (int i = 9; i >= 1; --i) { x[i] = x[i - 1] + ((current_k[i - 1] * u[i - 1]) / 32768); } x[0] = u[0]; // clipping, buffering if (u[0] > 511) { bufs[0][buf_count] += 511 << 6; } else if (u[0] < -511) { bufs[0][buf_count] += -511 << 6; } else { bufs[0][buf_count] += (u[0] << 6); } ++buf_count; --sample_count; ++pitch_count; if (pitch_count >= current_pitch) { pitch_count = 0; } --length; } // return; } phase_stop: switch (phase) { case PH_SETUP: if (sample_count <= length) { sample_count = 0; // pin_BSY = true; phase = PH_WAIT; } else { sample_count -= length; } break; case PH_END: if (sample_count <= length) { sample_count = 0; pin_BSY = false; phase = PH_IDLE; } else { sample_count -= length; } } // silent buffering //while (length > 0) { // bufs[0][buf_count++] += 0; // --length; //} } // setup parameteroption when RST=H void VLM5030::Impl::setupParameter(byte param) { // latch parameter value parameter = param; // bit 0,1 : 4800bps / 9600bps , interporator step if (param & 2) { // bit 1 = 1 , 9600bps interp_step = 4; // 9600bps : no interporator } else if (param & 1) { // bit1 = 0 & bit0 = 1 , 4800bps interp_step = 2; // 4800bps : 2 interporator } else { // bit1 = bit0 = 0 : 2400bps interp_step = 1; // 2400bps : 4 interporator } // bit 3,4,5 : speed (frame size) frame_size = VLM5030_speed_table[(param >> 3) & 7]; // bit 6,7 : low / high pitch if (param & 0x80) { // bit7=1 , high pitch pitch_offset = -8; } else if (param & 0x40) { // bit6=1 , low pitch pitch_offset = 8; } else { pitch_offset = 0; } } void VLM5030::Impl::reset() { phase = PH_RESET; address = 0; vcu_addr_h = 0; pin_BSY = false; old_energy = old_pitch = 0; new_energy = new_pitch = 0; current_energy = current_pitch = 0; target_energy = target_pitch = 0; memset(old_k, 0, sizeof(old_k)); memset(new_k, 0, sizeof(new_k)); memset(current_k, 0, sizeof(current_k)); memset(target_k, 0, sizeof(target_k)); interp_count = sample_count = pitch_count = 0; memset(x, 0, sizeof(x)); // reset parameters setupParameter(0x00); } // get BSY pin level bool VLM5030::Impl::getBSY(EmuTime::param time) { updateStream(time); return pin_BSY; } // latch control data void VLM5030::Impl::writeData(byte data) { latch_data = data; } void VLM5030::Impl::writeControl(byte data, EmuTime::param time) { updateStream(time); setRST((data & 0x01) != 0); setVCU((data & 0x04) != 0); setST ((data & 0x02) != 0); } // set RST pin level : reset / set table address A8-A15 void VLM5030::Impl::setRST(bool pin) { if (pin_RST) { if (!pin) { // H -> L : latch parameters pin_RST = false; setupParameter(latch_data); } } else { if (pin) { // L -> H : reset chip pin_RST = true; if (pin_BSY) { reset(); } } } } // set VCU pin level : ?? unknown void VLM5030::Impl::setVCU(bool pin) { // direct mode / indirect mode pin_VCU = pin; } // set ST pin level : set table address A0-A7 / start speech void VLM5030::Impl::setST(bool pin) { if (pin_ST == pin) { // pin level unchanged return; } if (!pin) { // H -> L pin_ST = false; if (pin_VCU) { // direct access mode & address High vcu_addr_h = (int(latch_data) << 8) + 0x01; } else { // check access mode if (vcu_addr_h) { // direct access mode address = (vcu_addr_h & 0xff00) + latch_data; vcu_addr_h = 0; } else { // indirect access mode int table = (latch_data & 0xfe) + ((int(latch_data) & 1) << 8); address = (((*rom)[(table + 0) & address_mask]) << 8) | (*rom)[(table + 1) & address_mask]; } // reset process status sample_count = frame_size; interp_count = FR_SIZE; // clear filter // start after 3 sampling cycle phase = PH_RUN; } } else { // L -> H pin_ST = true; // setup speech, BSY on after 30ms? phase = PH_SETUP; sample_count = 1; // wait time for busy on pin_BSY = true; } } VLM5030::Impl::Impl(const std::string& name, const std::string& desc, const std::string& romFilename, const DeviceConfig& config) : ResampledSoundDevice(config.getMotherBoard(), name, desc, 1) { XMLElement voiceROMconfig(name); voiceROMconfig.addAttribute("id", "name"); XMLElement romElement("rom"); romElement.addChild(XMLElement( // load by sha1sum "sha1", "4f36d139ee4baa7d5980f765de9895570ee05f40")); romElement.addChild(XMLElement( // load by predefined filename in software rom's dir "filename", FileOperations::stripExtension(romFilename) + "_voice.rom")); romElement.addChild(XMLElement( // or hardcoded filename in ditto dir "filename", "keyboardmaster/voice.rom")); voiceROMconfig.addChild(std::move(romElement)); rom = make_unique( name + " ROM", "rom", DeviceConfig(config, voiceROMconfig)); // reset input pins pin_RST = pin_ST = pin_VCU = false; latch_data = 0; reset(); phase = PH_IDLE; address_mask = rom->getSize() - 1; const int CLOCK_FREQ = 3579545; double input = CLOCK_FREQ / 440.0; setInputRate(int(input + 0.5)); registerSound(config); } VLM5030::Impl::~Impl() { unregisterSound(); } template void VLM5030::Impl::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("address_mask", address_mask); ar.serialize("frame_size", frame_size); ar.serialize("pitch_offset", pitch_offset); ar.serialize("current_energy", current_energy); ar.serialize("current_pitch", current_pitch); ar.serialize("current_k", current_k); ar.serialize("x", x); ar.serialize("address", address); ar.serialize("vcu_addr_h", vcu_addr_h); ar.serialize("old_k", old_k); ar.serialize("new_k", new_k); ar.serialize("target_k", target_k); ar.serialize("old_energy", old_energy); ar.serialize("new_energy", new_energy); ar.serialize("target_energy", target_energy); ar.serialize("old_pitch", old_pitch); ar.serialize("new_pitch", new_pitch); ar.serialize("target_pitch", target_pitch); ar.serialize("interp_step", interp_step); ar.serialize("interp_count", interp_count); ar.serialize("sample_count", sample_count); ar.serialize("pitch_count", pitch_count); ar.serialize("latch_data", latch_data); ar.serialize("parameter", parameter); ar.serialize("phase", phase); ar.serialize("pin_BSY", pin_BSY); ar.serialize("pin_ST", pin_ST); ar.serialize("pin_VCU", pin_VCU); ar.serialize("pin_RST", pin_RST); } // class VLM5030 VLM5030::VLM5030(const std::string& name, const std::string& desc, const std::string& romFilename, const DeviceConfig& config) : pimpl(make_unique(name, desc, romFilename, config)) { } VLM5030::~VLM5030() { } void VLM5030::reset() { pimpl->reset(); } void VLM5030::writeData(byte data) { pimpl->writeData(data); } void VLM5030::writeControl(byte data, EmuTime::param time) { pimpl->writeControl(data, time); } bool VLM5030::getBSY(EmuTime::param time) { return pimpl->getBSY(time); } template void VLM5030::serialize(Archive& ar, unsigned version) { pimpl->serialize(ar, version); } INSTANTIATE_SERIALIZE_METHODS(VLM5030); } // namespace openmsx openmsx-0.10.0/src/sound/AudioInputDevice.hh0000644000175000017500000000050412262345041021504 0ustar manuelmanuel00000000000000#ifndef AUDIOINPUTDEVICE_HH #define AUDIOINPUTDEVICE_HH #include "Pluggable.hh" namespace openmsx { class AudioInputDevice : public Pluggable { public: /** * Read wave data */ virtual short readSample(EmuTime::param time) = 0; // Pluggable virtual string_ref getClass() const; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/YM2151.cc0000644000175000017500000014766512262345041017153 0ustar manuelmanuel00000000000000/***************************************************************************** * * Yamaha YM2151 driver (version 2.150 final beta) * ******************************************************************************/ #include "YM2151.hh" #include "ResampledSoundDevice.hh" #include "EmuTimer.hh" #include "IRQHelper.hh" #include "DeviceConfig.hh" #include "serialize.hh" #include "memory.hh" #include #include namespace openmsx { class YM2151::Impl : public ResampledSoundDevice, private EmuTimerCallback { public: Impl(const std::string& name, const std::string& desc, const DeviceConfig& config, EmuTime::param time); ~Impl(); void reset(EmuTime::param time); void writeReg(byte r, byte v, EmuTime::param time); byte readStatus() const; template void serialize(Archive& ar, unsigned version); private: // a single operator struct YM2151Operator { template void serialize(Archive& ar, unsigned version); int* connect; // operator output 'direction' int* mem_connect; // where to put the delayed sample (MEM) unsigned phase; // accumulated operator phase unsigned freq; // operator frequency count int dt1; // current DT1 (detune 1 phase inc/decrement) value unsigned mul; // frequency count multiply unsigned dt1_i; // DT1 index * 32 unsigned dt2; // current DT2 (detune 2) value int mem_value; // delayed sample (MEM) value // channel specific data // note: each operator number 0 contains channel specific data unsigned fb_shift; // feedback shift value for operators 0 in each channel int fb_out_curr; // operator feedback value (used only by operators 0) int fb_out_prev; // previous feedback value (used only by operators 0) unsigned kc; // channel KC (copied to all operators) unsigned kc_i; // just for speedup unsigned pms; // channel PMS unsigned ams; // channel AMS unsigned AMmask; // LFO Amplitude Modulation enable mask unsigned state; // Envelope state: 4-attack(AR) // 3-decay(D1R) // 2-sustain(D2R) // 1-release(RR) // 0-off unsigned tl; // Total attenuation Level int volume; // current envelope attenuation level unsigned d1l; // envelope switches to sustain state after unsigned key; // 0=last key was KEY OFF, 1=last key was KEY ON unsigned ks; // key scale unsigned ar; // attack rate unsigned d1r; // decay rate unsigned d2r; // sustain rate unsigned rr; // release rate byte eg_sh_ar; // (attack state) byte eg_sel_ar; // (attack state) byte eg_sh_d1r; // (decay state) byte eg_sel_d1r; // (decay state) // reaching this level byte eg_sh_d2r; // (sustain state) byte eg_sel_d2r; // (sustain state) byte eg_sh_rr; // (release state) byte eg_sel_rr; // (release state) }; void setConnect(YM2151Operator* om1, int cha, int v); // SoundDevice virtual void generateChannels(int** bufs, unsigned num); void callback(byte flag); void setStatus(byte flags); void resetStatus(byte flags); void initTables(); void initChipTables(); // operator methods void envelopeKONKOFF(YM2151Operator* op, int v); static void refreshEG(YM2151Operator* op); int opCalc(YM2151Operator* op, unsigned env, int pm); int opCalc1(YM2151Operator* op, unsigned env, int pm); inline unsigned volumeCalc(YM2151Operator* op, unsigned AM); inline void keyOn(YM2151Operator* op, unsigned keySet); inline void keyOff(YM2151Operator* op, unsigned keyClear); // general chip mehods void chanCalc(unsigned chan); void chan7Calc(); void advanceEG(); void advance(); bool checkMuteHelper(); IRQHelper irq; // Timers (see EmuTimer class for details about timing) const std::unique_ptr timer1; const std::unique_ptr timer2; YM2151Operator oper[32]; // the 32 operators unsigned pan[16]; // channels output masks (0xffffffff = enable) unsigned eg_cnt; // global envelope generator counter unsigned eg_timer; // global envelope generator counter // works at frequency = chipclock/64/3 unsigned lfo_phase; // accumulated LFO phase (0 to 255) unsigned lfo_timer; // LFO timer unsigned lfo_overflow; // LFO generates new output when lfo_timer // reaches this value unsigned lfo_counter; // LFO phase increment counter unsigned lfo_counter_add;// step of lfo_counter unsigned lfa; // LFO current AM output int lfp; // LFO current PM output unsigned noise; // noise enable/period register // bit 7 - noise enable, bits 4-0 - noise period unsigned noise_rng; // 17 bit noise shift register int noise_p; // current noise 'phase' unsigned noise_f; // current noise period unsigned csm_req; // CSM KEY ON / KEY OFF sequence request unsigned irq_enable; // IRQ enable for timer B (bit 3) and timer A // (bit 2); bit 7 - CSM mode (keyon to all // slots, everytime timer A overflows) unsigned status; // chip status (BUSY, IRQ Flags) // Frequency-deltas to get the closest frequency possible. // There are 11 octaves because of DT2 (max 950 cents over base frequency) // and LFO phase modulation (max 800 cents below AND over base frequency) // Summary: octave explanation // 0 note code - LFO PM // 1 note code // 2 note code // 3 note code // 4 note code // 5 note code // 6 note code // 7 note code // 8 note code // 9 note code + DT2 + LFO PM // 10 note code + DT2 + LFO PM unsigned freq[11 * 768]; // 11 octaves, 768 'cents' per octave // No Save // Frequency deltas for DT1. These deltas alter operator frequency // after it has been taken from frequency-deltas table. int dt1_freq[8 * 32]; // 8 DT1 levels, 32 KC values // No Save unsigned noise_tab[32]; // 17bit Noise Generator periods // No Save int chanout[8]; int m2, c1, c2; // Phase Modulation input for operators 2,3,4 int mem; // one sample delay memory word timer_A_val; byte lfo_wsel; // LFO waveform (0-saw, 1-square, 2-triangle, // 3-random noise) byte amd; // LFO Amplitude Modulation Depth signed char pmd; // LFO Phase Modulation Depth byte test; // TEST register byte ct; // output control pins (bit1-CT2, bit0-CT1) byte regs[256]; // only used for serialization ATM }; // TODO void ym2151WritePortCallback(void* ref, unsigned port, byte value); static const int FREQ_SH = 16; // 16.16 fixed point (frequency calculations) static const int FREQ_MASK = (1 << FREQ_SH) - 1; static const int ENV_BITS = 10; static const int ENV_LEN = 1 << ENV_BITS; static const double ENV_STEP = 128.0 / ENV_LEN; static const int MAX_ATT_INDEX = ENV_LEN - 1; // 1023 static const int MIN_ATT_INDEX = 0; static const unsigned EG_ATT = 4; static const unsigned EG_DEC = 3; static const unsigned EG_SUS = 2; static const unsigned EG_REL = 1; static const unsigned EG_OFF = 0; static const int SIN_BITS = 10; static const int SIN_LEN = 1 << SIN_BITS; static const int SIN_MASK = SIN_LEN - 1; static const int TL_RES_LEN = 256; // 8 bits addressing (real chip) // TL_TAB_LEN is calculated as: // 13 - sinus amplitude bits (Y axis) // 2 - sinus sign bit (Y axis) // TL_RES_LEN - sinus resolution (X axis) static const unsigned TL_TAB_LEN = 13 * 2 * TL_RES_LEN; static int tl_tab[TL_TAB_LEN]; static const unsigned ENV_QUIET = TL_TAB_LEN >> 3; // sin waveform table in 'decibel' scale static unsigned sin_tab[SIN_LEN]; // translate from D1L to volume index (16 D1L levels) static unsigned d1l_tab[16]; static const unsigned RATE_STEPS = 8; static byte eg_inc[19 * RATE_STEPS] = { //cycle:0 1 2 3 4 5 6 7 /* 0 */ 0,1, 0,1, 0,1, 0,1, // rates 00..11 0 (increment by 0 or 1) /* 1 */ 0,1, 0,1, 1,1, 0,1, // rates 00..11 1 /* 2 */ 0,1, 1,1, 0,1, 1,1, // rates 00..11 2 /* 3 */ 0,1, 1,1, 1,1, 1,1, // rates 00..11 3 /* 4 */ 1,1, 1,1, 1,1, 1,1, // rate 12 0 (increment by 1) /* 5 */ 1,1, 1,2, 1,1, 1,2, // rate 12 1 /* 6 */ 1,2, 1,2, 1,2, 1,2, // rate 12 2 /* 7 */ 1,2, 2,2, 1,2, 2,2, // rate 12 3 /* 8 */ 2,2, 2,2, 2,2, 2,2, // rate 13 0 (increment by 2) /* 9 */ 2,2, 2,4, 2,2, 2,4, // rate 13 1 /*10 */ 2,4, 2,4, 2,4, 2,4, // rate 13 2 /*11 */ 2,4, 4,4, 2,4, 4,4, // rate 13 3 /*12 */ 4,4, 4,4, 4,4, 4,4, // rate 14 0 (increment by 4) /*13 */ 4,4, 4,8, 4,4, 4,8, // rate 14 1 /*14 */ 4,8, 4,8, 4,8, 4,8, // rate 14 2 /*15 */ 4,8, 8,8, 4,8, 8,8, // rate 14 3 /*16 */ 8,8, 8,8, 8,8, 8,8, // rates 15 0, 15 1, 15 2, 15 3 (increment by 8) /*17 */ 16,16,16,16,16,16,16,16, // rates 15 2, 15 3 for attack /*18 */ 0,0, 0,0, 0,0, 0,0, // infinity rates for attack and decay(s) }; #define O(a) (a*RATE_STEPS) // note that there is no O(17) in this table - it's directly in the code static byte eg_rate_select[32 + 64 + 32] = { // Envelope Generator rates (32 + 64 rates + 32 RKS) // 32 dummy (infinite time) rates O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18), // rates 00-11 O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), O( 0),O( 1),O( 2),O( 3), // rate 12 O( 4),O( 5),O( 6),O( 7), // rate 13 O( 8),O( 9),O(10),O(11), // rate 14 O(12),O(13),O(14),O(15), // rate 15 O(16),O(16),O(16),O(16), // 32 dummy rates (same as 15 3) O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16), O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16) }; #undef O // rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 // mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 #define O(a) (a*1) static byte eg_rate_shift[32 + 64 + 32] = { // Envelope Generator counter shifts (32 + 64 rates + 32 RKS) // 32 infinite time rates O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), // rates 00-11 O(11),O(11),O(11),O(11), O(10),O(10),O(10),O(10), O( 9),O( 9),O( 9),O( 9), O( 8),O( 8),O( 8),O( 8), O( 7),O( 7),O( 7),O( 7), O( 6),O( 6),O( 6),O( 6), O( 5),O( 5),O( 5),O( 5), O( 4),O( 4),O( 4),O( 4), O( 3),O( 3),O( 3),O( 3), O( 2),O( 2),O( 2),O( 2), O( 1),O( 1),O( 1),O( 1), O( 0),O( 0),O( 0),O( 0), // rate 12 O( 0),O( 0),O( 0),O( 0), // rate 13 O( 0),O( 0),O( 0),O( 0), // rate 14 O( 0),O( 0),O( 0),O( 0), // rate 15 O( 0),O( 0),O( 0),O( 0), // 32 dummy rates (same as 15 3) O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0), O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0) }; #undef O // DT2 defines offset in cents from base note // // This table defines offset in frequency-deltas table. // User's Manual page 22 // // Values below were calculated using formula: value = orig.val / 1.5625 // // DT2=0 DT2=1 DT2=2 DT2=3 // 0 600 781 950 static unsigned dt2_tab[4] = { 0, 384, 500, 608 }; // DT1 defines offset in Hertz from base note // This table is converted while initialization... // Detune table shown in YM2151 User's Manual is wrong (verified on the real chip) static byte dt1_tab[4 * 32] = { // DT1 = 0 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // DT1 = 1 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 8, 8, // DT1 = 2 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,16,16,16, // DT1 = 3 2, 2, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 8, 9,10,11,12,13,14,16,17,19,20,22,22,22,22 }; static word phaseinc_rom[768] = { 1299,1300,1301,1302,1303,1304,1305,1306,1308,1309,1310,1311,1313,1314,1315,1316, 1318,1319,1320,1321,1322,1323,1324,1325,1327,1328,1329,1330,1332,1333,1334,1335, 1337,1338,1339,1340,1341,1342,1343,1344,1346,1347,1348,1349,1351,1352,1353,1354, 1356,1357,1358,1359,1361,1362,1363,1364,1366,1367,1368,1369,1371,1372,1373,1374, 1376,1377,1378,1379,1381,1382,1383,1384,1386,1387,1388,1389,1391,1392,1393,1394, 1396,1397,1398,1399,1401,1402,1403,1404,1406,1407,1408,1409,1411,1412,1413,1414, 1416,1417,1418,1419,1421,1422,1423,1424,1426,1427,1429,1430,1431,1432,1434,1435, 1437,1438,1439,1440,1442,1443,1444,1445,1447,1448,1449,1450,1452,1453,1454,1455, 1458,1459,1460,1461,1463,1464,1465,1466,1468,1469,1471,1472,1473,1474,1476,1477, 1479,1480,1481,1482,1484,1485,1486,1487,1489,1490,1492,1493,1494,1495,1497,1498, 1501,1502,1503,1504,1506,1507,1509,1510,1512,1513,1514,1515,1517,1518,1520,1521, 1523,1524,1525,1526,1528,1529,1531,1532,1534,1535,1536,1537,1539,1540,1542,1543, 1545,1546,1547,1548,1550,1551,1553,1554,1556,1557,1558,1559,1561,1562,1564,1565, 1567,1568,1569,1570,1572,1573,1575,1576,1578,1579,1580,1581,1583,1584,1586,1587, 1590,1591,1592,1593,1595,1596,1598,1599,1601,1602,1604,1605,1607,1608,1609,1610, 1613,1614,1615,1616,1618,1619,1621,1622,1624,1625,1627,1628,1630,1631,1632,1633, 1637,1638,1639,1640,1642,1643,1645,1646,1648,1649,1651,1652,1654,1655,1656,1657, 1660,1661,1663,1664,1666,1667,1669,1670,1672,1673,1675,1676,1678,1679,1681,1682, 1685,1686,1688,1689,1691,1692,1694,1695,1697,1698,1700,1701,1703,1704,1706,1707, 1709,1710,1712,1713,1715,1716,1718,1719,1721,1722,1724,1725,1727,1728,1730,1731, 1734,1735,1737,1738,1740,1741,1743,1744,1746,1748,1749,1751,1752,1754,1755,1757, 1759,1760,1762,1763,1765,1766,1768,1769,1771,1773,1774,1776,1777,1779,1780,1782, 1785,1786,1788,1789,1791,1793,1794,1796,1798,1799,1801,1802,1804,1806,1807,1809, 1811,1812,1814,1815,1817,1819,1820,1822,1824,1825,1827,1828,1830,1832,1833,1835, 1837,1838,1840,1841,1843,1845,1846,1848,1850,1851,1853,1854,1856,1858,1859,1861, 1864,1865,1867,1868,1870,1872,1873,1875,1877,1879,1880,1882,1884,1885,1887,1888, 1891,1892,1894,1895,1897,1899,1900,1902,1904,1906,1907,1909,1911,1912,1914,1915, 1918,1919,1921,1923,1925,1926,1928,1930,1932,1933,1935,1937,1939,1940,1942,1944, 1946,1947,1949,1951,1953,1954,1956,1958,1960,1961,1963,1965,1967,1968,1970,1972, 1975,1976,1978,1980,1982,1983,1985,1987,1989,1990,1992,1994,1996,1997,1999,2001, 2003,2004,2006,2008,2010,2011,2013,2015,2017,2019,2021,2022,2024,2026,2028,2029, 2032,2033,2035,2037,2039,2041,2043,2044,2047,2048,2050,2052,2054,2056,2058,2059, 2062,2063,2065,2067,2069,2071,2073,2074,2077,2078,2080,2082,2084,2086,2088,2089, 2092,2093,2095,2097,2099,2101,2103,2104,2107,2108,2110,2112,2114,2116,2118,2119, 2122,2123,2125,2127,2129,2131,2133,2134,2137,2139,2141,2142,2145,2146,2148,2150, 2153,2154,2156,2158,2160,2162,2164,2165,2168,2170,2172,2173,2176,2177,2179,2181, 2185,2186,2188,2190,2192,2194,2196,2197,2200,2202,2204,2205,2208,2209,2211,2213, 2216,2218,2220,2222,2223,2226,2227,2230,2232,2234,2236,2238,2239,2242,2243,2246, 2249,2251,2253,2255,2256,2259,2260,2263,2265,2267,2269,2271,2272,2275,2276,2279, 2281,2283,2285,2287,2288,2291,2292,2295,2297,2299,2301,2303,2304,2307,2308,2311, 2315,2317,2319,2321,2322,2325,2326,2329,2331,2333,2335,2337,2338,2341,2342,2345, 2348,2350,2352,2354,2355,2358,2359,2362,2364,2366,2368,2370,2371,2374,2375,2378, 2382,2384,2386,2388,2389,2392,2393,2396,2398,2400,2402,2404,2407,2410,2411,2414, 2417,2419,2421,2423,2424,2427,2428,2431,2433,2435,2437,2439,2442,2445,2446,2449, 2452,2454,2456,2458,2459,2462,2463,2466,2468,2470,2472,2474,2477,2480,2481,2484, 2488,2490,2492,2494,2495,2498,2499,2502,2504,2506,2508,2510,2513,2516,2517,2520, 2524,2526,2528,2530,2531,2534,2535,2538,2540,2542,2544,2546,2549,2552,2553,2556, 2561,2563,2565,2567,2568,2571,2572,2575,2577,2579,2581,2583,2586,2589,2590,2593 }; // Noise LFO waveform. // // Here are just 256 samples out of much longer data. // // It does NOT repeat every 256 samples on real chip and I wasnt able to find // the point where it repeats (even in strings as long as 131072 samples). // // I only put it here because its better than nothing and perhaps // someone might be able to figure out the real algorithm. // // Note that (due to the way the LFO output is calculated) it is quite // possible that two values: 0x80 and 0x00 might be wrong in this table. // To be exact: // some 0x80 could be 0x81 as well as some 0x00 could be 0x01. static byte lfo_noise_waveform[256] = { 0xFF,0xEE,0xD3,0x80,0x58,0xDA,0x7F,0x94,0x9E,0xE3,0xFA,0x00,0x4D,0xFA,0xFF,0x6A, 0x7A,0xDE,0x49,0xF6,0x00,0x33,0xBB,0x63,0x91,0x60,0x51,0xFF,0x00,0xD8,0x7F,0xDE, 0xDC,0x73,0x21,0x85,0xB2,0x9C,0x5D,0x24,0xCD,0x91,0x9E,0x76,0x7F,0x20,0xFB,0xF3, 0x00,0xA6,0x3E,0x42,0x27,0x69,0xAE,0x33,0x45,0x44,0x11,0x41,0x72,0x73,0xDF,0xA2, 0x32,0xBD,0x7E,0xA8,0x13,0xEB,0xD3,0x15,0xDD,0xFB,0xC9,0x9D,0x61,0x2F,0xBE,0x9D, 0x23,0x65,0x51,0x6A,0x84,0xF9,0xC9,0xD7,0x23,0xBF,0x65,0x19,0xDC,0x03,0xF3,0x24, 0x33,0xB6,0x1E,0x57,0x5C,0xAC,0x25,0x89,0x4D,0xC5,0x9C,0x99,0x15,0x07,0xCF,0xBA, 0xC5,0x9B,0x15,0x4D,0x8D,0x2A,0x1E,0x1F,0xEA,0x2B,0x2F,0x64,0xA9,0x50,0x3D,0xAB, 0x50,0x77,0xE9,0xC0,0xAC,0x6D,0x3F,0xCA,0xCF,0x71,0x7D,0x80,0xA6,0xFD,0xFF,0xB5, 0xBD,0x6F,0x24,0x7B,0x00,0x99,0x5D,0xB1,0x48,0xB0,0x28,0x7F,0x80,0xEC,0xBF,0x6F, 0x6E,0x39,0x90,0x42,0xD9,0x4E,0x2E,0x12,0x66,0xC8,0xCF,0x3B,0x3F,0x10,0x7D,0x79, 0x00,0xD3,0x1F,0x21,0x93,0x34,0xD7,0x19,0x22,0xA2,0x08,0x20,0xB9,0xB9,0xEF,0x51, 0x99,0xDE,0xBF,0xD4,0x09,0x75,0xE9,0x8A,0xEE,0xFD,0xE4,0x4E,0x30,0x17,0xDF,0xCE, 0x11,0xB2,0x28,0x35,0xC2,0x7C,0x64,0xEB,0x91,0x5F,0x32,0x0C,0x6E,0x00,0xF9,0x92, 0x19,0xDB,0x8F,0xAB,0xAE,0xD6,0x12,0xC4,0x26,0x62,0xCE,0xCC,0x0A,0x03,0xE7,0xDD, 0xE2,0x4D,0x8A,0xA6,0x46,0x95,0x0F,0x8F,0xF5,0x15,0x97,0x32,0xD4,0x28,0x1E,0x55 }; void YM2151::Impl::initTables() { for (int x = 0; x < TL_RES_LEN; ++x) { double m = (1 << 16) / pow(2, (x + 1) * (ENV_STEP / 4.0) / 8.0); m = floor(m); // we never reach (1 << 16) here due to the (x + 1) // result fits within 16 bits at maximum int n = int(m); // 16 bits here n >>= 4; // 12 bits here if (n & 1) { // round to closest n = (n >> 1) + 1; } else { n = n >> 1; } // 11 bits here (rounded) n <<= 2; // 13 bits here (as in real chip) tl_tab[x * 2 + 0] = n; tl_tab[x * 2 + 1] = -tl_tab[x * 2 + 0]; for (int i = 1; i < 13; ++i) { tl_tab[x * 2 + 0 + i * 2 * TL_RES_LEN] = tl_tab[x * 2 + 0] >> i; tl_tab[x * 2 + 1 + i * 2 * TL_RES_LEN] = -tl_tab[x * 2 + 0 + i * 2 * TL_RES_LEN]; } } for (int i = 0; i < SIN_LEN; ++i) { // non-standard sinus double m = sin((i * 2 + 1) * M_PI / SIN_LEN); // verified on the real chip // we never reach zero here due to (i * 2 + 1) double o; if (m > 0.0) { // convert to decibels o = 8 * log( 1.0 / m) / log(2.0); } else { o = 8 * log(-1.0 / m) / log(2.0); } o = o / (ENV_STEP / 4); int n = int(2.0 * o); if (n & 1) { // round to closest n = (n >> 1) + 1; } else { n = n >> 1; } sin_tab[i] = n * 2 + (m >= 0.0 ? 0 : 1); } // calculate d1l_tab table for (int i = 0; i < 16; ++i) { // every 3 'dB' except for all bits = 1 = 45+48 'dB' double m = unsigned((i != 15 ? i : i + 16) * (4.0 / ENV_STEP)); d1l_tab[i] = unsigned(m); } } void YM2151::Impl::initChipTables() { // this loop calculates Hertz values for notes from c-0 to b-7 // including 64 'cents' (100/64 that is 1.5625 of real cent) per note // i*100/64/1200 is equal to i/768 // real chip works with 10 bits fixed point values (10.10) // -10 because phaseinc_rom table values are already in 10.10 format double mult = 1 << (FREQ_SH - 10); for (int i = 0; i < 768; ++i) { double phaseinc = phaseinc_rom[i]; // real chip phase increment // octave 2 - reference octave // adjust to X.10 fixed point freq[768 + 2 * 768 + i] = int(phaseinc * mult) & 0xffffffc0; // octave 0 and octave 1 for (int j = 0; j < 2; ++j) { // adjust to X.10 fixed point freq[768 + j * 768 + i] = (freq[768 + 2 * 768 + i] >> (2 - j)) & 0xffffffc0; } // octave 3 to 7 for (int j = 3; j < 8; ++j) { freq[768 + j * 768 + i] = freq[768 + 2 * 768 + i] << (j - 2); } } // octave -1 (all equal to: oct 0, _KC_00_, _KF_00_) for (int i = 0; i < 768; ++i) { freq[0 * 768 + i] = freq[1 * 768 + 0]; } // octave 8 and 9 (all equal to: oct 7, _KC_14_, _KF_63_) for (int j = 8; j < 10; ++j) { for (int i = 0; i < 768; ++i) { freq[768 + j * 768 + i] = freq[768 + 8 * 768 - 1]; } } mult = 1 << FREQ_SH; for (int j = 0; j < 4; ++j) { for (int i = 0; i < 32; ++i) { // calculate phase increment double phaseinc = double(dt1_tab[j * 32 + i]) / (1 << 20) * (SIN_LEN); // positive and negative values dt1_freq[(j + 0) * 32 + i] = int(phaseinc * mult); dt1_freq[(j + 4) * 32 + i] = -dt1_freq[(j + 0) * 32 + i]; } } timer_A_val = 0; // calculate noise periods table // this table tells how many cycles/samples it takes before noise is recalculated. // 2/2 means every cycle/sample, 2/5 means 2 out of 5 cycles/samples, etc. for (int i = 0; i < 32; ++i) { noise_tab[i] = 32 - (i != 31 ? i : 30); // rate 30 and 31 are the same } } void YM2151::Impl::keyOn(YM2151Operator* op, unsigned keySet) { if (!op->key) { op->phase = 0; /* clear phase */ op->state = EG_ATT; /* KEY ON = attack */ op->volume += (~op->volume * (eg_inc[op->eg_sel_ar + ((eg_cnt >> op->eg_sh_ar)&7)]) ) >>4; if (op->volume <= MIN_ATT_INDEX) { op->volume = MIN_ATT_INDEX; op->state = EG_DEC; } } op->key |= keySet; } void YM2151::Impl::keyOff(YM2151Operator* op, unsigned keyClear) { if (op->key) { op->key &= keyClear; if (!op->key) { if (op->state > EG_REL) { op->state = EG_REL; /* KEY OFF = release */ } } } } void YM2151::Impl::envelopeKONKOFF(YM2151Operator* op, int v) { if (v & 0x08) { // M1 keyOn (op + 0, 1); } else { keyOff(op + 0,unsigned(~1)); } if (v & 0x20) { // M2 keyOn (op + 1, 1); } else { keyOff(op + 1,unsigned(~1)); } if (v & 0x10) { // C1 keyOn (op + 2, 1); } else { keyOff(op + 2,unsigned(~1)); } if (v & 0x40) { // C2 keyOn (op + 3, 1); } else { keyOff(op + 3,unsigned(~1)); } } void YM2151::Impl::setConnect(YM2151Operator* om1, int cha, int v) { YM2151Operator* om2 = om1 + 1; YM2151Operator* oc1 = om1 + 2; // set connect algorithm // MEM is simply one sample delay switch (v & 7) { case 0: // M1---C1---MEM---M2---C2---OUT om1->connect = &c1; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &m2; break; case 1: // M1------+-MEM---M2---C2---OUT // C1-+ om1->connect = &mem; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &m2; break; case 2: // M1-----------------+-C2---OUT // C1---MEM---M2-+ om1->connect = &c2; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &m2; break; case 3: // M1---C1---MEM------+-C2---OUT // M2-+ om1->connect = &c1; oc1->connect = &mem; om2->connect = &c2; om1->mem_connect = &c2; break; case 4: // M1---C1-+-OUT // M2---C2-+ // MEM: not used om1->connect = &c1; oc1->connect = &chanout[cha]; om2->connect = &c2; om1->mem_connect = &mem; // store it anywhere where it will not be used break; case 5: // +----C1----+ // M1-+-MEM---M2-+-OUT // +----C2----+ om1->connect = nullptr; // special mark oc1->connect = &chanout[cha]; om2->connect = &chanout[cha]; om1->mem_connect = &m2; break; case 6: // M1---C1-+ // M2-+-OUT // C2-+ // MEM: not used om1->connect = &c1; oc1->connect = &chanout[cha]; om2->connect = &chanout[cha]; om1->mem_connect = &mem; // store it anywhere where it will not be used break; case 7: // M1-+ // C1-+-OUT // M2-+ // C2-+ // MEM: not used om1->connect = &chanout[cha]; oc1->connect = &chanout[cha]; om2->connect = &chanout[cha]; om1->mem_connect = &mem; // store it anywhere where it will not be used break; } } void YM2151::Impl::refreshEG(YM2151Operator* op) { unsigned kc = op->kc; // v = 32 + 2*RATE + RKS = max 126 unsigned v = kc >> op->ks; if ((op->ar + v) < 32 + 62) { op->eg_sh_ar = eg_rate_shift [op->ar + v]; op->eg_sel_ar = eg_rate_select[op->ar + v]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17 * RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r = eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r = eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; op += 1; v = kc >> op->ks; if ((op->ar + v) < 32 + 62) { op->eg_sh_ar = eg_rate_shift [op->ar + v]; op->eg_sel_ar = eg_rate_select[op->ar + v]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17 * RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r = eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r = eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; op += 1; v = kc >> op->ks; if ((op->ar + v) < 32 + 62) { op->eg_sh_ar = eg_rate_shift [op->ar + v]; op->eg_sel_ar = eg_rate_select[op->ar + v]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17 * RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r = eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r = eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; op += 1; v = kc >> op->ks; if ((op->ar + v) < 32 + 62) { op->eg_sh_ar = eg_rate_shift [op->ar + v]; op->eg_sel_ar = eg_rate_select[op->ar + v]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17 * RATE_STEPS; } op->eg_sh_d1r = eg_rate_shift [op->d1r + v]; op->eg_sel_d1r = eg_rate_select[op->d1r + v]; op->eg_sh_d2r = eg_rate_shift [op->d2r + v]; op->eg_sel_d2r = eg_rate_select[op->d2r + v]; op->eg_sh_rr = eg_rate_shift [op->rr + v]; op->eg_sel_rr = eg_rate_select[op->rr + v]; } void YM2151::Impl::writeReg(byte r, byte v, EmuTime::param time) { updateStream(time); YM2151Operator* op = &oper[(r & 0x07) * 4 + ((r & 0x18) >> 3)]; regs[r] = v; switch (r & 0xe0) { case 0x00: switch (r) { case 0x01: // LFO reset(bit 1), Test Register (other bits) test = v; if (v & 2) lfo_phase = 0; break; case 0x08: envelopeKONKOFF(&oper[(v & 7) * 4], v); break; case 0x0f: // noise mode enable, noise period noise = v; noise_f = noise_tab[v & 0x1f]; noise_p = 0; break; case 0x10: timer_A_val &= 0x03; timer_A_val |= v << 2; timer1->setValue(timer_A_val); break; case 0x11: timer_A_val &= 0x03fc; timer_A_val |= v & 3; timer1->setValue(timer_A_val); break; case 0x12: timer2->setValue(v); break; case 0x14: // CSM, irq flag reset, irq enable, timer start/stop irq_enable = v; // bit 3-timer B, bit 2-timer A, bit 7 - CSM if (v & 0x10) { // reset timer A irq flag resetStatus(1); } if (v & 0x20) { // reset timer B irq flag resetStatus(2); } timer1->setStart((v & 4) != 0, time); timer2->setStart((v & 8) != 0, time); break; case 0x18: // LFO frequency lfo_overflow = (1 << ((15 - (v >> 4)) + 3)); lfo_counter_add = 0x10 + (v & 0x0f); break; case 0x19: // PMD (bit 7==1) or AMD (bit 7==0) if (v & 0x80) { pmd = v & 0x7f; } else { amd = v & 0x7f; } break; case 0x1b: // CT2, CT1, LFO waveform ct = v >> 6; lfo_wsel = v & 3; // TODO ym2151WritePortCallback(0 , ct); break; default: break; } break; case 0x20: op = &oper[(r & 7) * 4]; switch (r & 0x18) { case 0x00: // RL enable, Feedback, Connection op->fb_shift = ((v >> 3) & 7) ? ((v >> 3) & 7) + 6 : 0; pan[(r & 7) * 2 + 0] = (v & 0x40) ? ~0 : 0; pan[(r & 7) * 2 + 1] = (v & 0x80) ? ~0 : 0; setConnect(op, r & 7, v & 7); break; case 0x08: // Key Code v &= 0x7f; if (v != op->kc) { unsigned kc_channel = (v - (v>>2))*64; kc_channel += 768; kc_channel |= (op->kc_i & 63); (op + 0)->kc = v; (op + 0)->kc_i = kc_channel; (op + 1)->kc = v; (op + 1)->kc_i = kc_channel; (op + 2)->kc = v; (op + 2)->kc_i = kc_channel; (op + 3)->kc = v; (op + 3)->kc_i = kc_channel; unsigned kc = v>>2; (op + 0)->dt1 = dt1_freq[(op + 0)->dt1_i + kc]; (op + 0)->freq = ((freq[kc_channel + (op + 0)->dt2] + (op + 0)->dt1) * (op + 0)->mul) >> 1; (op + 1)->dt1 = dt1_freq[(op + 1)->dt1_i + kc]; (op + 1)->freq = ((freq[kc_channel + (op + 1)->dt2] + (op + 1)->dt1) * (op + 1)->mul) >> 1; (op + 2)->dt1 = dt1_freq[(op + 2)->dt1_i + kc]; (op + 2)->freq = ((freq[kc_channel + (op + 2)->dt2] + (op + 2)->dt1) * (op + 2)->mul) >> 1; (op + 3)->dt1 = dt1_freq[(op + 3)->dt1_i + kc]; (op + 3)->freq = ((freq[kc_channel + (op + 3)->dt2] + (op + 3)->dt1) * (op + 3)->mul) >> 1; refreshEG( op ); } break; case 0x10: // Key Fraction v >>= 2; if (v != (op->kc_i & 63)) { unsigned kc_channel = v; kc_channel |= (op->kc_i & ~63); (op + 0)->kc_i = kc_channel; (op + 1)->kc_i = kc_channel; (op + 2)->kc_i = kc_channel; (op + 3)->kc_i = kc_channel; (op + 0)->freq = ((freq[kc_channel + (op + 0)->dt2] + (op + 0)->dt1) * (op + 0)->mul) >> 1; (op + 1)->freq = ((freq[kc_channel + (op + 1)->dt2] + (op + 1)->dt1) * (op + 1)->mul) >> 1; (op + 2)->freq = ((freq[kc_channel + (op + 2)->dt2] + (op + 2)->dt1) * (op + 2)->mul) >> 1; (op + 3)->freq = ((freq[kc_channel + (op + 3)->dt2] + (op + 3)->dt1) * (op + 3)->mul) >> 1; } break; case 0x18: // PMS, AMS op->pms = (v >> 4) & 7; op->ams = (v & 3); break; } break; case 0x40: { // DT1, MUL unsigned olddt1_i = op->dt1_i; unsigned oldmul = op->mul; op->dt1_i = (v & 0x70) << 1; op->mul = (v & 0x0f) ? (v & 0x0f) << 1 : 1; if (olddt1_i != op->dt1_i) { op->dt1 = dt1_freq[ op->dt1_i + (op->kc>>2) ]; } if ((olddt1_i != op->dt1_i) || (oldmul != op->mul)) { op->freq = ((freq[op->kc_i + op->dt2] + op->dt1) * op->mul) >> 1; } break; } case 0x60: // TL op->tl = (v & 0x7f) << (ENV_BITS - 7); // 7bit TL break; case 0x80: { // KS, AR unsigned oldks = op->ks; unsigned oldar = op->ar; op->ks = 5 - (v >> 6); op->ar = (v & 0x1f) ? 32 + ((v & 0x1f) << 1) : 0; if ((op->ar != oldar) || (op->ks != oldks)) { if ((op->ar + (op->kc >> op->ks)) < 32 + 62) { op->eg_sh_ar = eg_rate_shift [op->ar + (op->kc>>op->ks)]; op->eg_sel_ar = eg_rate_select[op->ar + (op->kc>>op->ks)]; } else { op->eg_sh_ar = 0; op->eg_sel_ar = 17 * RATE_STEPS; } } if (op->ks != oldks) { op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc >> op->ks)]; op->eg_sel_d1r = eg_rate_select[op->d1r + (op->kc >> op->ks)]; op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc >> op->ks)]; op->eg_sel_d2r = eg_rate_select[op->d2r + (op->kc >> op->ks)]; op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc >> op->ks)]; op->eg_sel_rr = eg_rate_select[op->rr + (op->kc >> op->ks)]; } break; } case 0xa0: // LFO AM enable, D1R op->AMmask = (v & 0x80) ? ~0 : 0; op->d1r = (v & 0x1f) ? 32 + ((v & 0x1f) << 1) : 0; op->eg_sh_d1r = eg_rate_shift [op->d1r + (op->kc >> op->ks)]; op->eg_sel_d1r = eg_rate_select[op->d1r + (op->kc >> op->ks)]; break; case 0xc0: { // DT2, D2R unsigned olddt2 = op->dt2; op->dt2 = dt2_tab[v >> 6]; if (op->dt2 != olddt2) { op->freq = ((freq[op->kc_i + op->dt2] + op->dt1) * op->mul) >> 1; } op->d2r = (v & 0x1f) ? 32 + ((v & 0x1f) << 1) : 0; op->eg_sh_d2r = eg_rate_shift [op->d2r + (op->kc >> op->ks)]; op->eg_sel_d2r = eg_rate_select[op->d2r + (op->kc >> op->ks)]; break; } case 0xe0: // D1L, RR op->d1l = d1l_tab[v >> 4]; op->rr = 34 + ((v & 0x0f) << 2); op->eg_sh_rr = eg_rate_shift [op->rr + (op->kc >> op->ks)]; op->eg_sel_rr = eg_rate_select[op->rr + (op->kc >> op->ks)]; break; } } YM2151::Impl::Impl(const std::string& name, const std::string& desc, const DeviceConfig& config, EmuTime::param time) : ResampledSoundDevice(config.getMotherBoard(), name, desc, 8, true) , irq(config.getMotherBoard(), getName() + ".IRQ") , timer1(EmuTimer::createOPM_1(config.getScheduler(), *this)) , timer2(EmuTimer::createOPM_2(config.getScheduler(), *this)) { // Avoid UMR on savestate // TODO Registers 0x20-0xFF are cleared on reset. // Should we do the same for registers 0x00-0x1F? memset(regs, 0, sizeof(regs)); initTables(); initChipTables(); static const int CLCK_FREQ = 3579545; double input = CLCK_FREQ / 64.0; setInputRate(int(input + 0.5)); reset(time); registerSound(config); } YM2151::Impl::~Impl() { unregisterSound(); } bool YM2151::Impl::checkMuteHelper() { for (int i = 0; i < 32; ++i) { if (oper[i].state != EG_OFF) { return false; } } return true; } void YM2151::Impl::reset(EmuTime::param time) { // initialize hardware registers for (int i = 0; i < 32; ++i) { memset(&oper[i], '\0', sizeof(oper[i])); oper[i].volume = MAX_ATT_INDEX; oper[i].kc_i = 768; // min kc_i value } eg_timer = 0; eg_cnt = 0; lfo_timer = 0; lfo_counter = 0; lfo_phase = 0; lfo_wsel = 0; pmd = 0; amd = 0; lfa = 0; lfp = 0; test = 0; irq_enable = 0; timer1->setStart(0, time); timer2->setStart(0, time); noise = 0; noise_rng = 0; noise_p = 0; noise_f = noise_tab[0]; csm_req = 0; status = 0; writeReg(0x1b, 0, time); // only because of CT1, CT2 output pins writeReg(0x18, 0, time); // set LFO frequency for (int i = 0x20; i < 0x100; ++i) { // set the operators writeReg(i, 0, time); } irq.reset(); } int YM2151::Impl::opCalc(YM2151Operator* OP, unsigned env, int pm) { unsigned p = (env << 3) + sin_tab[(int((OP->phase & ~FREQ_MASK) + (pm << 15)) >> FREQ_SH) & SIN_MASK]; if (p >= TL_TAB_LEN) { return 0; } return tl_tab[p]; } int YM2151::Impl::opCalc1(YM2151Operator* OP, unsigned env, int pm) { int i = (OP->phase & ~FREQ_MASK) + pm; unsigned p = (env << 3) + sin_tab[(i >> FREQ_SH) & SIN_MASK]; if (p >= TL_TAB_LEN) { return 0; } return tl_tab[p]; } unsigned YM2151::Impl::volumeCalc(YM2151Operator* OP, unsigned AM) { return OP->tl + unsigned(OP->volume) + (AM & OP->AMmask); } void YM2151::Impl::chanCalc(unsigned chan) { m2 = c1 = c2 = mem = 0; YM2151Operator* op = &oper[chan*4]; // M1 *op->mem_connect = op->mem_value; // restore delayed sample (MEM) value to m2 or c2 unsigned AM = 0; if (op->ams) { AM = lfa << (op->ams-1); } unsigned env = volumeCalc(op, AM); { int out = op->fb_out_prev + op->fb_out_curr; op->fb_out_prev = op->fb_out_curr; if (!op->connect) { // algorithm 5 mem = c1 = c2 = op->fb_out_prev; } else { *op->connect = op->fb_out_prev; } op->fb_out_curr = 0; if (env < ENV_QUIET) { if (!op->fb_shift) { out = 0; } op->fb_out_curr = opCalc1(op, env, (out << op->fb_shift)); } } env = volumeCalc(op + 1, AM); // M2 if (env < ENV_QUIET) { *(op + 1)->connect += opCalc(op + 1, env, m2); } env = volumeCalc(op + 2, AM); // C1 if (env < ENV_QUIET) { *(op + 2)->connect += opCalc(op + 2, env, c1); } env = volumeCalc(op + 3, AM); // C2 if (env < ENV_QUIET) { chanout[chan] += opCalc(op + 3, env, c2); } // M1 op->mem_value = mem; } void YM2151::Impl::chan7Calc() { m2 = c1 = c2 = mem = 0; YM2151Operator* op = &oper[7 * 4]; // M1 *op->mem_connect = op->mem_value; // restore delayed sample (MEM) value to m2 or c2 unsigned AM = 0; if (op->ams) { AM = lfa << (op->ams - 1); } unsigned env = volumeCalc(op, AM); { int out = op->fb_out_prev + op->fb_out_curr; op->fb_out_prev = op->fb_out_curr; if (!op->connect) { // algorithm 5 mem = c1 = c2 = op->fb_out_prev; } else { // other algorithms *op->connect = op->fb_out_prev; } op->fb_out_curr = 0; if (env < ENV_QUIET) { if (!op->fb_shift) { out = 0; } op->fb_out_curr = opCalc1(op, env, (out << op->fb_shift)); } } env = volumeCalc(op + 1, AM); // M2 if (env < ENV_QUIET) { *(op + 1)->connect += opCalc(op + 1, env, m2); } env = volumeCalc(op + 2, AM); // C1 if (env < ENV_QUIET) { *(op + 2)->connect += opCalc(op + 2, env, c1); } env = volumeCalc(op + 3, AM); // C2 if (noise & 0x80) { unsigned noiseout = 0; if (env < 0x3ff) { noiseout = (env ^ 0x3ff) * 2; // range of the YM2151 noise output is -2044 to 2040 } chanout[7] += (noise_rng & 0x10000) ? noiseout : unsigned(-int(noiseout)); // bit 16 -> output } else { if (env < ENV_QUIET) { chanout[7] += opCalc(op + 3, env, c2); } } // M1 op->mem_value = mem; } /* The 'rate' is calculated from following formula (example on decay rate): rks = notecode after key scaling (a value from 0 to 31) DR = value written to the chip register rate = 2*DR + rks; (max rate = 2*31+31 = 93) Four MSBs of the 'rate' above are the 'main' rate (from 00 to 15) Two LSBs of the 'rate' above are the value 'x' (the shape type). (eg. '11 2' means that 'rate' is 11*4+2=46) NOTE: A 'sample' in the description below is actually 3 output samples, thats because the Envelope Generator clock is equal to internal_clock/3. Single '-' (minus) character in the diagrams below represents one sample on the output; this is for rates 11 x (11 0, 11 1, 11 2 and 11 3) these 'main' rates: 00 x: single '-' = 2048 samples; (ie. level can change every 2048 samples) 01 x: single '-' = 1024 samples; 02 x: single '-' = 512 samples; 03 x: single '-' = 256 samples; 04 x: single '-' = 128 samples; 05 x: single '-' = 64 samples; 06 x: single '-' = 32 samples; 07 x: single '-' = 16 samples; 08 x: single '-' = 8 samples; 09 x: single '-' = 4 samples; 10 x: single '-' = 2 samples; 11 x: single '-' = 1 sample; (ie. level can change every 1 sample) Shapes for rates 11 x look like this: rate: step: 11 0 01234567 level: 0 -- 1 -- 2 -- 3 -- rate: step: 11 1 01234567 level: 0 -- 1 -- 2 - 3 - 4 -- rate: step: 11 2 01234567 level: 0 -- 1 - 2 - 3 -- 4 - 5 - rate: step: 11 3 01234567 level: 0 -- 1 - 2 - 3 - 4 - 5 - 6 - For rates 12 x, 13 x, 14 x and 15 x output level changes on every sample - this means that the waveform looks like this: (but the level changes by different values on different steps) 12 3 01234567 0 - 2 - 4 - 8 - 10 - 12 - 14 - 18 - 20 - Notes about the timing: ---------------------- 1. Synchronism Output level of each two (or more) voices running at the same 'main' rate (eg 11 0 and 11 1 in the diagram below) will always be changing in sync, even if there're started with some delay. Note that, in the diagram below, the decay phase in channel 0 starts at sample #2, while in channel 1 it starts at sample #6. Anyway, both channels will always change their levels at exactly the same (following) samples. (S - start point of this channel, A-attack phase, D-decay phase): step: 01234567012345670123456 channel 0: -- | -- | - | - | -- | -- | -- | - | - | -- AADDDDDDDDDDDDDDDD S 01234567012345670123456 channel 1: - | - | -- | -- | -- | - | - | -- | -- | -- AADDDDDDDDDDDDDDDD S 01234567012345670123456 2. Shifted (delayed) synchronism Output of each two (or more) voices running at different 'main' rate (9 1, 10 1 and 11 1 in the diagrams below) will always be changing in 'delayed-sync' (even if there're started with some delay as in "1.") Note that the shapes are delayed by exactly one sample per one 'main' rate increment. (Normally one would expect them to start at the same samples.) See diagram below (* - start point of the shape). cycle: 0123456701234567012345670123456701234567012345670123456701234567 rate 09 1 *------- -------- ---- ---- -------- *------- | -------- | ---- | ---- | -------- rate 10 1 | -- | *--- | ---- | -- | -- | ---- | *--- | | ---- | | -- | | <- one step (two samples) delay between 9 1 and 10 1 | -- | | | ----| | *--- | ---- | -- | -- | ---- rate 11 1 | - | -- | *- | -- | - | - | -- | *- | -- | - || <- one step (one sample) delay between 10 1 and 11 1 - || --| *- -- - - -- *- -- - - -- */ void YM2151::Impl::advanceEG() { if (eg_timer++ != 3) { // envelope generator timer overlfows every 3 samples (on real chip) return; } eg_timer = 0; eg_cnt++; // envelope generator for (int i = 0; i < 32; ++i) { YM2151Operator& op = oper[i]; switch (op.state) { case EG_ATT: // attack phase if (!(eg_cnt & ((1 << op.eg_sh_ar) - 1))) { op.volume += (~op.volume * (eg_inc[op.eg_sel_ar + ((eg_cnt >> op.eg_sh_ar) & 7)]) ) >> 4; if (op.volume <= MIN_ATT_INDEX) { op.volume = MIN_ATT_INDEX; op.state = EG_DEC; } } break; case EG_DEC: // decay phase if (!(eg_cnt & ((1 << op.eg_sh_d1r) - 1))) { op.volume += eg_inc[op.eg_sel_d1r + ((eg_cnt >> op.eg_sh_d1r) & 7)]; if (unsigned(op.volume) >= op.d1l) { op.state = EG_SUS; } } break; case EG_SUS: // sustain phase if (!(eg_cnt & ((1 << op.eg_sh_d2r) - 1))) { op.volume += eg_inc[op.eg_sel_d2r + ((eg_cnt >> op.eg_sh_d2r) & 7)]; if (op.volume >= MAX_ATT_INDEX) { op.volume = MAX_ATT_INDEX; op.state = EG_OFF; } } break; case EG_REL: // release phase if (!(eg_cnt & ((1 << op.eg_sh_rr) - 1))) { op.volume += eg_inc[op.eg_sel_rr + ((eg_cnt >> op.eg_sh_rr) & 7)]; if (op.volume >= MAX_ATT_INDEX) { op.volume = MAX_ATT_INDEX; op.state = EG_OFF; } } break; } } } void YM2151::Impl::advance() { // LFO if (test & 2) { lfo_phase = 0; } else { if (lfo_timer++ >= lfo_overflow) { lfo_timer = 0; lfo_counter += lfo_counter_add; lfo_phase += (lfo_counter >> 4); lfo_phase &= 255; lfo_counter &= 15; } } unsigned i = lfo_phase; // calculate LFO AM and PM waveform value (all verified on real chip, // except for noise algorithm which is impossible to analyse) int a, p; switch (lfo_wsel) { case 0: // saw // AM: 255 down to 0 // PM: 0 to 127, -127 to 0 (at PMD=127: LFP = 0 to 126, -126 to 0) a = 255 - i; if (i < 128) { p = i; } else { p = i - 255; } break; case 1: // square // AM: 255, 0 // PM: 128,-128 (LFP = exactly +PMD, -PMD) if (i < 128) { a = 255; p = 128; } else { a = 0; p = -128; } break; case 2: // triangle // AM: 255 down to 1 step -2; 0 up to 254 step +2 // PM: 0 to 126 step +2, 127 to 1 step -2, // 0 to -126 step -2, -127 to -1 step +2 if (i < 128) { a = 255 - (i * 2); } else { a = (i * 2) - 256; } if (i < 64) { // i = 0..63 p = i * 2; // 0 to 126 step +2 } else if (i < 128) { // i = 64..127 p = 255 - i * 2; // 127 to 1 step -2 } else if (i < 192) { // i = 128..191 p = 256 - i*2; // 0 to -126 step -2 } else { // i = 192..255 p = i*2 - 511; // -127 to -1 step +2 } break; case 3: default: // keep the compiler happy // Random. The real algorithm is unknown !!! // We just use a snapshot of data from real chip // AM: range 0 to 255 // PM: range -128 to 127 a = lfo_noise_waveform[i]; p = a - 128; break; } lfa = a * amd / 128; lfp = p * pmd / 128; // The Noise Generator of the YM2151 is 17-bit shift register. // Input to the bit16 is negated (bit0 XOR bit3) (EXNOR). // Output of the register is negated (bit0 XOR bit3). // Simply use bit16 as the noise output. // noise changes depending on the index in noise_tab (noise_f = noise_tab[x]) // noise_tab contains how many cycles/samples (x2) the noise should change. // so, when it contains 29, noise should change every 14.5 cycles (2 out of 29). // if you read this code well, you'll see that is what happens here :) noise_p -= 2; if (noise_p < 0) { noise_p += noise_f; unsigned j = ((noise_rng ^ (noise_rng >> 3)) & 1) ^ 1; noise_rng = (j << 16) | (noise_rng >> 1); } // phase generator YM2151Operator* op = &oper[0]; // CH 0 M1 i = 8; do { // only when phase modulation from LFO is enabled for this channel if (op->pms) { int mod_ind = lfp; // -128..+127 (8bits signed) if (op->pms < 6) { mod_ind >>= (6 - op->pms); } else { mod_ind <<= (op->pms - 5); } if (mod_ind) { unsigned kc_channel = op->kc_i + mod_ind; (op + 0)->phase += ((freq[kc_channel + (op + 0)->dt2] + (op + 0)->dt1) * (op + 0)->mul) >> 1; (op + 1)->phase += ((freq[kc_channel + (op + 1)->dt2] + (op + 1)->dt1) * (op + 1)->mul) >> 1; (op + 2)->phase += ((freq[kc_channel + (op + 2)->dt2] + (op + 2)->dt1) * (op + 2)->mul) >> 1; (op + 3)->phase += ((freq[kc_channel + (op + 3)->dt2] + (op + 3)->dt1) * (op + 3)->mul) >> 1; } else { // phase modulation from LFO is equal to zero (op + 0)->phase += (op + 0)->freq; (op + 1)->phase += (op + 1)->freq; (op + 2)->phase += (op + 2)->freq; (op + 3)->phase += (op + 3)->freq; } } else { // phase modulation from LFO is disabled (op + 0)->phase += (op + 0)->freq; (op + 1)->phase += (op + 1)->freq; (op + 2)->phase += (op + 2)->freq; (op + 3)->phase += (op + 3)->freq; } op += 4; i--; } while (i); // CSM is calculated *after* the phase generator calculations (verified // on real chip) // CSM keyon line seems to be ORed with the KO line inside of the chip. // The result is that it only works when KO (register 0x08) is off, ie. 0 // // Interesting effect is that when timer A is set to 1023, the KEY ON happens // on every sample, so there is no KEY OFF at all - the result is that // the sound played is the same as after normal KEY ON. if (csm_req) { // CSM KEYON/KEYOFF seqeunce request if (csm_req == 2) { // KEY ON op = &oper[0]; // CH 0 M1 i = 32; do { keyOn(op, 2); op++; i--; } while (i); csm_req = 1; } else { // KEY OFF op = &oper[0]; // CH 0 M1 i = 32; do { keyOff(op,unsigned(~2)); op++; i--; } while (i); csm_req = 0; } } } void YM2151::Impl::generateChannels(int** bufs, unsigned num) { if (checkMuteHelper()) { // TODO update internal state, even if muted for (int i = 0; i < 8; ++i) { bufs[i] = nullptr; } return; } for (unsigned i = 0; i < num; ++i) { advanceEG(); for (int j = 0; j < 8-1; ++j) { chanout[j] = 0; chanCalc(j); } chanout[7] = 0; chan7Calc(); // special case for channel 7 for (int j = 0; j < 8; ++j) { bufs[j][2 * i + 0] += chanout[j] & pan[2 * j + 0]; bufs[j][2 * i + 1] += chanout[j] & pan[2 * j + 1]; } advance(); } } void YM2151::Impl::callback(byte flag) { if (flag & 0x20) { // Timer 1 if (irq_enable & 0x04) { setStatus(1); } if (irq_enable & 0x80) { csm_req = 2; // request KEY ON / KEY OFF sequence } } if (flag & 0x40) { // Timer 2 if (irq_enable & 0x08) { setStatus(2); } } } byte YM2151::Impl::readStatus() const { return status; } void YM2151::Impl::setStatus(byte flags) { status |= flags; if (status) { irq.set(); } } void YM2151::Impl::resetStatus(byte flags) { status &= ~flags; if (!status) { irq.reset(); } } template void YM2151::Impl::YM2151Operator::serialize(Archive& ar, unsigned /*version*/) { //int* connect; // recalculated from regs[0x20-0x27] //int* mem_connect; // recalculated from regs[0x20-0x27] ar.serialize("phase", phase); ar.serialize("freq", freq); ar.serialize("dt1", dt1); ar.serialize("mul", mul); ar.serialize("dt1_i", dt1_i); ar.serialize("dt2", dt2); ar.serialize("mem_value", mem_value); //ar.serialize("fb_shift", fb_shift); // recalculated from regs[0x20-0x27] ar.serialize("fb_out_curr", fb_out_curr); ar.serialize("fb_out_prev", fb_out_prev); ar.serialize("kc", kc); ar.serialize("kc_i", kc_i); ar.serialize("pms", pms); ar.serialize("ams", ams); ar.serialize("AMmask", AMmask); ar.serialize("state", state); ar.serialize("tl", tl); ar.serialize("volume", volume); ar.serialize("d1l", d1l); ar.serialize("key", key); ar.serialize("ks", ks); ar.serialize("ar", this->ar); ar.serialize("d1r", d1r); ar.serialize("d2r", d2r); ar.serialize("rr", rr); ar.serialize("eg_sh_ar", eg_sh_ar); ar.serialize("eg_sel_ar", eg_sel_ar); ar.serialize("eg_sh_d1r", eg_sh_d1r); ar.serialize("eg_sel_d1r", eg_sel_d1r); ar.serialize("eg_sh_d2r", eg_sh_d2r); ar.serialize("eg_sel_d2r", eg_sel_d2r); ar.serialize("eg_sh_rr", eg_sh_rr); ar.serialize("eg_sel_rr", eg_sel_rr); }; template void YM2151::Impl::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("irq", irq); ar.serialize("timer1", *timer1); ar.serialize("timer2", *timer2); ar.serialize("operators", oper); //ar.serialize("pan", pan); // recalculated from regs[0x20-0x27] ar.serialize("eg_cnt", eg_cnt); ar.serialize("eg_timer", eg_timer); ar.serialize("lfo_phase", lfo_phase); ar.serialize("lfo_timer", lfo_timer); ar.serialize("lfo_overflow", lfo_overflow); ar.serialize("lfo_counter", lfo_counter); ar.serialize("lfo_counter_add", lfo_counter_add); ar.serialize("lfa", lfa); ar.serialize("lfp", lfp); ar.serialize("noise", noise); ar.serialize("noise_rng", noise_rng); ar.serialize("noise_p", noise_p); ar.serialize("noise_f", noise_f); ar.serialize("csm_req", csm_req); ar.serialize("irq_enable", irq_enable); ar.serialize("status", status); ar.serialize("chanout", chanout); ar.serialize("m2", m2); ar.serialize("c1", c1); ar.serialize("c2", c2); ar.serialize("mem", mem); ar.serialize("timer_A_val", timer_A_val); ar.serialize("lfo_wsel", lfo_wsel); ar.serialize("amd", amd); ar.serialize("pmd", pmd); ar.serialize("test", test); ar.serialize("ct", ct); ar.serialize_blob("registers", regs, sizeof(regs)); if (ar.isLoader()) { // TODO restore more state from registers EmuTime::param time = timer1->getCurrentTime(); for (int r = 0x20; r < 0x28; ++r) { writeReg(r , regs[r], time); } } } // YM2151 YM2151::YM2151(const std::string& name, const std::string& desc, const DeviceConfig& config, EmuTime::param time) : pimpl(make_unique(name, desc, config, time)) { } YM2151::~YM2151() { } void YM2151::reset(EmuTime::param time) { pimpl->reset(time); } void YM2151::writeReg(byte r, byte v, EmuTime::param time) { pimpl->writeReg(r, v, time); } byte YM2151::readStatus() const { return pimpl->readStatus(); } template void YM2151::serialize(Archive& ar, unsigned version) { pimpl->serialize(ar, version); } INSTANTIATE_SERIALIZE_METHODS(YM2151); } // namespace openmsx openmsx-0.10.0/src/sound/SDLSoundDriver.cc0000644000175000017500000001071712262345041021107 0ustar manuelmanuel00000000000000#include "SDLSoundDriver.hh" #include "Reactor.hh" #include "MSXMotherBoard.hh" #include "RealTime.hh" #include "GlobalSettings.hh" #include "ThrottleManager.hh" #include "MSXException.hh" #include "Math.hh" #include "StringOp.hh" #include "Timer.hh" #include "build-info.hh" #include #include #include #include namespace openmsx { SDLSoundDriver::SDLSoundDriver(Reactor& reactor_, unsigned wantedFreq, unsigned wantedSamples) : reactor(reactor_) , muted(true) { SDL_AudioSpec desired; desired.freq = wantedFreq; desired.samples = Math::powerOfTwo(wantedSamples); desired.channels = 2; // stereo desired.format = OPENMSX_BIGENDIAN ? AUDIO_S16MSB : AUDIO_S16LSB; desired.callback = audioCallbackHelper; // must be a static method desired.userdata = this; if (SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { throw MSXException(StringOp::Builder() << "Unable to initialize SDL audio subsystem: " << SDL_GetError()); } SDL_AudioSpec audioSpec; if (SDL_OpenAudio(&desired, &audioSpec) != 0) { SDL_QuitSubSystem(SDL_INIT_AUDIO); throw MSXException(StringOp::Builder() << "Unable to open SDL audio: " << SDL_GetError()); } frequency = audioSpec.freq; fragmentSize = audioSpec.samples; unsigned bufferSize = 3 * (audioSpec.size / sizeof(short)) + 2; mixBuffer.resize(bufferSize); reInit(); } SDLSoundDriver::~SDLSoundDriver() { SDL_CloseAudio(); SDL_QuitSubSystem(SDL_INIT_AUDIO); } void SDLSoundDriver::reInit() { SDL_LockAudio(); readIdx = 0; writeIdx = 0; SDL_UnlockAudio(); } void SDLSoundDriver::mute() { if (!muted) { muted = true; SDL_PauseAudio(1); } } void SDLSoundDriver::unmute() { if (muted) { muted = false; reInit(); SDL_PauseAudio(0); } } unsigned SDLSoundDriver::getFrequency() const { return frequency; } unsigned SDLSoundDriver::getSamples() const { return fragmentSize; } void SDLSoundDriver::audioCallbackHelper(void* userdata, byte* strm, int len) { assert((len & 3) == 0); // stereo, 16-bit static_cast(userdata)-> audioCallback(reinterpret_cast(strm), len / sizeof(short)); } unsigned SDLSoundDriver::getBufferFilled() const { int result = writeIdx - readIdx; if (result < 0) result += unsigned(mixBuffer.size()); assert((0 <= result) && (unsigned(result) < mixBuffer.size())); return result; } unsigned SDLSoundDriver::getBufferFree() const { // we can't distinguish completely filled from completely empty // (in both cases readIx would be equal to writeIdx), so instead // we define full as '(writeIdx + 2) == readIdx' (note that index // increases in steps of 2 (stereo)). int result = unsigned(mixBuffer.size()) - 2 - getBufferFilled(); assert((0 <= result) && (unsigned(result) < mixBuffer.size())); return result; } void SDLSoundDriver::audioCallback(short* stream, unsigned len) { assert((len & 1) == 0); // stereo unsigned available = getBufferFilled(); //PRT_DEBUG("DEBUG callback: " << available); unsigned num = std::min(len, available); if ((readIdx + num) < mixBuffer.size()) { memcpy(stream, &mixBuffer[readIdx], num * sizeof(short)); readIdx += num; } else { unsigned len1 = unsigned(mixBuffer.size()) - readIdx; memcpy(stream, &mixBuffer[readIdx], len1 * sizeof(short)); unsigned len2 = num - len1; memcpy(&stream[len1], &mixBuffer[0], len2 * sizeof(short)); readIdx = len2; } int missing = len - available; if (missing > 0) { // buffer underrun //PRT_DEBUG("DEBUG underrun: " << missing); memset(&stream[available], 0, missing * sizeof(short)); } } void SDLSoundDriver::uploadBuffer(short* buffer, unsigned len) { SDL_LockAudio(); len *= 2; // stereo unsigned free = getBufferFree(); if (len > free) { if (reactor.getGlobalSettings().getThrottleManager().isThrottled()) { do { SDL_UnlockAudio(); Timer::sleep(5000); // 5ms SDL_LockAudio(); if (MSXMotherBoard* board = reactor.getMotherBoard()) { board->getRealTime().resync(); } free = getBufferFree(); } while (len > free); } else { // drop excess samples len = free; } } assert(len <= free); if ((writeIdx + len) < mixBuffer.size()) { memcpy(&mixBuffer[writeIdx], buffer, len * sizeof(short)); writeIdx += len; } else { unsigned len1 = unsigned(mixBuffer.size()) - writeIdx; memcpy(&mixBuffer[writeIdx], buffer, len1 * sizeof(short)); unsigned len2 = len - len1; memcpy(&mixBuffer[0], &buffer[len1], len2 * sizeof(short)); writeIdx = len2; } SDL_UnlockAudio(); } } // namespace openmsx openmsx-0.10.0/src/sound/AudioInputConnector.cc0000644000175000017500000000215112262345041022225 0ustar manuelmanuel00000000000000#include "AudioInputConnector.hh" #include "DummyAudioInputDevice.hh" #include "AudioInputDevice.hh" #include "checked_cast.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { AudioInputConnector::AudioInputConnector(PluggingController& pluggingController, string_ref name) : Connector(pluggingController, name, make_unique()) { } AudioInputConnector::~AudioInputConnector() { } const std::string AudioInputConnector::getDescription() const { return "Audio input connector"; } string_ref AudioInputConnector::getClass() const { return "Audio Input Port"; } short AudioInputConnector::readSample(EmuTime::param time) const { return getPluggedAudioDev().readSample(time); } AudioInputDevice& AudioInputConnector::getPluggedAudioDev() const { return *checked_cast(&getPlugged()); } template void AudioInputConnector::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); } INSTANTIATE_SERIALIZE_METHODS(AudioInputConnector); } // namespace openmsx openmsx-0.10.0/src/sound/YM2413Burczynski.cc0000644000175000017500000011305412262345041021221 0ustar manuelmanuel00000000000000/* * * File: ym2413.c - software implementation of YM2413 * FM sound generator type OPLL * * Copyright (C) 2002 Jarek Burczynski * * Version 1.0 * * * TODO: * - make sure of the sinus amplitude bits * - make sure of the EG resolution bits (looks like the biggest * modulation index generated by the modulator is 123, 124 = no modulation) * - find proper algorithm for attack phase of EG * - tune up instruments ROM * - support sample replay in test mode (it is NOT as simple as setting bit 0 * in register 0x0f and using register 0x10 for sample data). * Which games use this feature ? */ #include "YM2413Burczynski.hh" #include "serialize.hh" #include #include namespace openmsx { namespace YM2413Burczynski { // envelope output entries static const int ENV_BITS = 10; static const double ENV_STEP = 128.0 / (1 << ENV_BITS); static const int MAX_ATT_INDEX = (1 << (ENV_BITS - 2)) - 1; // 255 static const int MIN_ATT_INDEX = 0; // sinwave entries static const int SIN_BITS = 10; static const int SIN_LEN = 1 << SIN_BITS; static const int SIN_MASK = SIN_LEN - 1; static const int TL_RES_LEN = 256; // 8 bits addressing (real chip) // key scale level // table is 3dB/octave, DV converts this into 6dB/octave // 0.1875 is bit 0 weight of the envelope counter (volume) expressed // in the 'decibel' scale #define DV(x) int(x / 0.1875) static const int ksl_tab[8 * 16] = { // OCT 0 DV( 0.000),DV( 0.000),DV( 0.000),DV( 0.000), DV( 0.000),DV( 0.000),DV( 0.000),DV( 0.000), DV( 0.000),DV( 0.000),DV( 0.000),DV( 0.000), DV( 0.000),DV( 0.000),DV( 0.000),DV( 0.000), // OCT 1 DV( 0.000),DV( 0.000),DV( 0.000),DV( 0.000), DV( 0.000),DV( 0.000),DV( 0.000),DV( 0.000), DV( 0.000),DV( 0.750),DV( 1.125),DV( 1.500), DV( 1.875),DV( 2.250),DV( 2.625),DV( 3.000), // OCT 2 DV( 0.000),DV( 0.000),DV( 0.000),DV( 0.000), DV( 0.000),DV( 1.125),DV( 1.875),DV( 2.625), DV( 3.000),DV( 3.750),DV( 4.125),DV( 4.500), DV( 4.875),DV( 5.250),DV( 5.625),DV( 6.000), // OCT 3 DV( 0.000),DV( 0.000),DV( 0.000),DV( 1.875), DV( 3.000),DV( 4.125),DV( 4.875),DV( 5.625), DV( 6.000),DV( 6.750),DV( 7.125),DV( 7.500), DV( 7.875),DV( 8.250),DV( 8.625),DV( 9.000), // OCT 4 DV( 0.000),DV( 0.000),DV( 3.000),DV( 4.875), DV( 6.000),DV( 7.125),DV( 7.875),DV( 8.625), DV( 9.000),DV( 9.750),DV(10.125),DV(10.500), DV(10.875),DV(11.250),DV(11.625),DV(12.000), // OCT 5 DV( 0.000),DV( 3.000),DV( 6.000),DV( 7.875), DV( 9.000),DV(10.125),DV(10.875),DV(11.625), DV(12.000),DV(12.750),DV(13.125),DV(13.500), DV(13.875),DV(14.250),DV(14.625),DV(15.000), // OCT 6 DV( 0.000),DV( 6.000),DV( 9.000),DV(10.875), DV(12.000),DV(13.125),DV(13.875),DV(14.625), DV(15.000),DV(15.750),DV(16.125),DV(16.500), DV(16.875),DV(17.250),DV(17.625),DV(18.000), // OCT 7 DV( 0.000),DV( 9.000),DV(12.000),DV(13.875), DV(15.000),DV(16.125),DV(16.875),DV(17.625), DV(18.000),DV(18.750),DV(19.125),DV(19.500), DV(19.875),DV(20.250),DV(20.625),DV(21.000) }; #undef DV // sustain level table (3dB per step) // 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,45 (dB) #define SC(db) int((double(db)) / ENV_STEP) static const int sl_tab[16] = { SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7), SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(15) }; #undef SC static const byte eg_inc[15][8] = { // cycle: 0 1 2 3 4 5 6 7 /* 0 */ { 0,1, 0,1, 0,1, 0,1, }, // rates 00..12 0 (increment by 0 or 1) /* 1 */ { 0,1, 0,1, 1,1, 0,1, }, // rates 00..12 1 /* 2 */ { 0,1, 1,1, 0,1, 1,1, }, // rates 00..12 2 /* 3 */ { 0,1, 1,1, 1,1, 1,1, }, // rates 00..12 3 /* 4 */ { 1,1, 1,1, 1,1, 1,1, }, // rate 13 0 (increment by 1) /* 5 */ { 1,1, 1,2, 1,1, 1,2, }, // rate 13 1 /* 6 */ { 1,2, 1,2, 1,2, 1,2, }, // rate 13 2 /* 7 */ { 1,2, 2,2, 1,2, 2,2, }, // rate 13 3 /* 8 */ { 2,2, 2,2, 2,2, 2,2, }, // rate 14 0 (increment by 2) /* 9 */ { 2,2, 2,4, 2,2, 2,4, }, // rate 14 1 /*10 */ { 2,4, 2,4, 2,4, 2,4, }, // rate 14 2 /*11 */ { 2,4, 4,4, 2,4, 4,4, }, // rate 14 3 /*12 */ { 4,4, 4,4, 4,4, 4,4, }, // rates 15 0, 15 1, 15 2, 15 3 (incr by 4) /*13 */ { 8,8, 8,8, 8,8, 8,8, }, // rates 15 2, 15 3 for attack /*14 */ { 0,0, 0,0, 0,0, 0,0, }, // infinity rates for attack and decay(s) }; // note that there is no value 13 in this table - it's directly in the code static const byte eg_rate_select[16 + 64 + 16] = { // Envelope Generator rates (16 + 64 rates + 16 RKS) // 16 infinite time rates 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, // rates 00-12 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, // rate 13 4, 5, 6, 7, // rate 14 8, 9,10,11, // rate 15 12,12,12,12, // 16 dummy rates (same as 15 3) 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, }; // rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // shift 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 // mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 static const byte eg_rate_shift[16 + 64 + 16] = { // Envelope Generator counter shifts (16 + 64 rates + 16 RKS) // 16 infinite time rates 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // rates 00-12 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, // rate 13 0, 0, 0, 0, // rate 14 0, 0, 0, 0, // rate 15 0, 0, 0, 0, // 16 dummy rates (same as 15 3) 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; // multiple table #define ML(x) byte(2 * x) static const byte mul_tab[16] = { ML( 0.50), ML( 1.00), ML( 2.00), ML( 3.00), ML( 4.00), ML( 5.00), ML( 6.00), ML( 7.00), ML( 8.00), ML( 9.00), ML(10.00), ML(10.00), ML(12.00), ML(12.00), ML(15.00), ML(15.00), }; #undef ML // TL_TAB_LEN is calculated as: // 11 - sinus amplitude bits (Y axis) // 2 - sinus sign bit (Y axis) // TL_RES_LEN - sinus resolution (X axis) const int TL_TAB_LEN = 11 * 2 * TL_RES_LEN; static int tl_tab[TL_TAB_LEN]; // sin waveform table in 'decibel' scale // two waveforms on OPLL type chips static unsigned sin_tab[SIN_LEN * 2]; // LFO Amplitude Modulation table (verified on real YM3812) // 27 output levels (triangle waveform); // 1 level takes one of: 192, 256 or 448 samples // // Length: 210 elements. // // Each of the elements has to be repeated // exactly 64 times (on 64 consecutive samples). // The whole table takes: 64 * 210 = 13440 samples. // // We use data>>1, until we find what it really is on real chip... static const int LFO_AM_TAB_ELEMENTS = 210; static const byte lfo_am_table[LFO_AM_TAB_ELEMENTS] = { 0,0,0,0,0,0,0, 1,1,1,1, 2,2,2,2, 3,3,3,3, 4,4,4,4, 5,5,5,5, 6,6,6,6, 7,7,7,7, 8,8,8,8, 9,9,9,9, 10,10,10,10, 11,11,11,11, 12,12,12,12, 13,13,13,13, 14,14,14,14, 15,15,15,15, 16,16,16,16, 17,17,17,17, 18,18,18,18, 19,19,19,19, 20,20,20,20, 21,21,21,21, 22,22,22,22, 23,23,23,23, 24,24,24,24, 25,25,25,25, 26,26,26, 25,25,25,25, 24,24,24,24, 23,23,23,23, 22,22,22,22, 21,21,21,21, 20,20,20,20, 19,19,19,19, 18,18,18,18, 17,17,17,17, 16,16,16,16, 15,15,15,15, 14,14,14,14, 13,13,13,13, 12,12,12,12, 11,11,11,11, 10,10,10,10, 9,9,9,9, 8,8,8,8, 7,7,7,7, 6,6,6,6, 5,5,5,5, 4,4,4,4, 3,3,3,3, 2,2,2,2, 1,1,1,1 }; // LFO Phase Modulation table (verified on real YM2413) static const signed char lfo_pm_table[8][8] = { // FNUM2/FNUM = 0 00xxxxxx (0x0000) { 0, 0, 0, 0, 0, 0, 0, 0, }, // FNUM2/FNUM = 0 01xxxxxx (0x0040) { 1, 0, 0, 0,-1, 0, 0, 0, }, // FNUM2/FNUM = 0 10xxxxxx (0x0080) { 2, 1, 0,-1,-2,-1, 0, 1, }, // FNUM2/FNUM = 0 11xxxxxx (0x00C0) { 3, 1, 0,-1,-3,-1, 0, 1, }, // FNUM2/FNUM = 1 00xxxxxx (0x0100) { 4, 2, 0,-2,-4,-2, 0, 2, }, // FNUM2/FNUM = 1 01xxxxxx (0x0140) { 5, 2, 0,-2,-5,-2, 0, 2, }, // FNUM2/FNUM = 1 10xxxxxx (0x0180) { 6, 3, 0,-3,-6,-3, 0, 3, }, // FNUM2/FNUM = 1 11xxxxxx (0x01C0) { 7, 3, 0,-3,-7,-3, 0, 3, }, }; // This is not 100% perfect yet but very close // // - multi parameters are 100% correct (instruments and drums) // - LFO PM and AM enable are 100% correct // - waveform DC and DM select are 100% correct static const byte table[16 + 3][8] = { // MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR // 0 1 2 3 4 5 6 7 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // user instrument { 0x61, 0x61, 0x1e, 0x17, 0xf0, 0x7f, 0x00, 0x17 }, // violin { 0x13, 0x41, 0x16, 0x0e, 0xfd, 0xf4, 0x23, 0x23 }, // guitar { 0x03, 0x01, 0x9a, 0x04, 0xf3, 0xf3, 0x13, 0xf3 }, // piano { 0x11, 0x61, 0x0e, 0x07, 0xfa, 0x64, 0x70, 0x17 }, // flute { 0x22, 0x21, 0x1e, 0x06, 0xf0, 0x76, 0x00, 0x28 }, // clarinet { 0x21, 0x22, 0x16, 0x05, 0xf0, 0x71, 0x00, 0x18 }, // oboe { 0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x17, 0x17 }, // trumpet { 0x23, 0x21, 0x2d, 0x16, 0x90, 0x90, 0x00, 0x07 }, // organ { 0x21, 0x21, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, // horn { 0x21, 0x21, 0x0b, 0x1a, 0x85, 0xa0, 0x70, 0x07 }, // synthesizer { 0x23, 0x01, 0x83, 0x10, 0xff, 0xb4, 0x10, 0xf4 }, // harpsichord { 0x97, 0xc1, 0x20, 0x07, 0xff, 0xf4, 0x22, 0x22 }, // vibraphone { 0x61, 0x00, 0x0c, 0x05, 0xc2, 0xf6, 0x40, 0x44 }, // synthesizer bass { 0x01, 0x01, 0x56, 0x03, 0x94, 0xc2, 0x03, 0x12 }, // acoustic bass { 0x21, 0x01, 0x89, 0x03, 0xf1, 0xe4, 0xf0, 0x23 }, // electric guitar // drum instruments definitions // MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR // 0 1 2 3 4 5 6 7 //{ 0x07, 0x21, 0x14, 0x00, 0xee, 0xf8, 0xff, 0xf8 }, //{ 0x01, 0x31, 0x00, 0x00, 0xf8, 0xf7, 0xf8, 0xf7 }, //{ 0x25, 0x11, 0x00, 0x00, 0xf8, 0xfa, 0xf8, 0x55 } { 0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },// BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) { 0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },// HH(multi verified), SD(multi not used) { 0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },// TOM(multi,env verified), TOP CYM(multi verified, env verified) }; static inline FreqIndex fnumToIncrement(int block_fnum) { // OPLL (YM2413) phase increment counter = 18bit // Chip works with 10.10 fixed point, while we use 16.16. const int block = (block_fnum & 0x1C00) >> 10; return FreqIndex(block_fnum & 0x03FF) >> (11 - block); } inline int Slot::calc_envelope(Channel& channel, unsigned eg_cnt, bool carrier) { switch (state) { case EG_DUMP: // Dump phase is performed by both operators in each channel. // When CARRIER envelope gets down to zero level, phases in BOTH // operators are reset (at the same time?). // TODO: That sounds logical, but it does not match the implementation. if (!(eg_cnt & eg_mask_dp)) { egout += eg_sel_dp[(eg_cnt >> eg_sh_dp) & 7]; if (egout >= MAX_ATT_INDEX) { egout = MAX_ATT_INDEX; setEnvelopeState(EG_ATTACK); phase = FreqIndex(0); // restart Phase Generator } } break; case EG_ATTACK: if (!(eg_cnt & eg_mask_ar)) { egout += (~egout * eg_sel_ar[(eg_cnt >> eg_sh_ar) & 7]) >> 2; if (egout <= MIN_ATT_INDEX) { egout = MIN_ATT_INDEX; setEnvelopeState(EG_DECAY); } } break; case EG_DECAY: if (!(eg_cnt & eg_mask_dr)) { egout += eg_sel_dr[(eg_cnt >> eg_sh_dr) & 7]; if (egout >= sl) { setEnvelopeState(EG_SUSTAIN); } } break; case EG_SUSTAIN: // this is important behaviour: // one can change percusive/non-percussive modes on the fly and // the chip will remain in sustain phase // - verified on real YM3812 if (eg_sustain) { // non-percussive mode (sustained tone) // do nothing } else { // percussive mode // during sustain phase chip adds Release Rate (in // percussive mode) if (!(eg_cnt & eg_mask_rr)) { egout += eg_sel_rr[(eg_cnt >> eg_sh_rr) & 7]; if (egout >= MAX_ATT_INDEX) { egout = MAX_ATT_INDEX; } } // else do nothing in sustain phase } break; case EG_RELEASE: // Exclude modulators in melody channels from performing anything in // this mode. if (carrier) { const bool sustain = !eg_sustain || channel.isSustained(); const unsigned mask = sustain ? eg_mask_rs : eg_mask_rr; if (!(eg_cnt & mask)) { const byte shift = sustain ? eg_sh_rs : eg_sh_rr; const byte* sel = sustain ? eg_sel_rs : eg_sel_rr; egout += sel[(eg_cnt >> shift) & 7]; if (egout >= MAX_ATT_INDEX) { egout = MAX_ATT_INDEX; setEnvelopeState(EG_OFF); } } } break; case EG_OFF: break; } return egout; } inline int Slot::calc_phase(Channel& channel, unsigned lfo_pm) { if (vib) { const int lfo_fn_table_index_offset = lfo_pm_table [(channel.getBlockFNum() & 0x01FF) >> 6][lfo_pm]; phase += fnumToIncrement( channel.getBlockFNum() * 2 + lfo_fn_table_index_offset ) * mul; } else { // LFO phase modulation disabled for this operator phase += freq; } return phase.toInt(); } inline void Slot::updateTotalLevel(Channel& channel) { TLL = TL + (channel.getKeyScaleLevelBase() >> ksl); } inline void Slot::updateAttackRate(int kcodeScaled) { if ((ar + kcodeScaled) < (16 + 62)) { eg_sh_ar = eg_rate_shift[ar + kcodeScaled]; eg_sel_ar = eg_inc[eg_rate_select[ar + kcodeScaled]]; } else { eg_sh_ar = 0; eg_sel_ar = eg_inc[13]; } eg_mask_ar = (1 << eg_sh_ar) - 1; } inline void Slot::updateDecayRate(int kcodeScaled) { eg_sh_dr = eg_rate_shift[dr + kcodeScaled]; eg_sel_dr = eg_inc[eg_rate_select[dr + kcodeScaled]]; eg_mask_dr = (1 << eg_sh_dr) - 1; } inline void Slot::updateReleaseRate(int kcodeScaled) { eg_sh_rr = eg_rate_shift[rr + kcodeScaled]; eg_sel_rr = eg_inc[eg_rate_select[rr + kcodeScaled]]; eg_mask_rr = (1 << eg_sh_rr) - 1; } inline int Slot::calcOutput(Channel& channel, unsigned eg_cnt, bool carrier, unsigned lfo_am, int phase) { int egout = calc_envelope(channel, eg_cnt, carrier); int env = (TLL + egout + (lfo_am & AMmask)) << 5; int p = env + wavetable[phase & SIN_MASK]; return p < TL_TAB_LEN ? tl_tab[p] : 0; } inline int Slot::calc_slot_mod(Channel& channel, unsigned eg_cnt, bool carrier, unsigned lfo_pm, unsigned lfo_am) { // Compute phase. int phase = calc_phase(channel, lfo_pm); if (fb_shift) { phase += (op1_out[0] + op1_out[1]) >> fb_shift; } // Shift output in 2-place buffer. op1_out[0] = op1_out[1]; // Calculate operator output. op1_out[1] = calcOutput(channel, eg_cnt, carrier, lfo_am, phase); return op1_out[0] << 1; } inline int Channel::calcOutput(unsigned eg_cnt, unsigned lfo_pm, unsigned lfo_am, int fm) { int phase = car.calc_phase(*this, lfo_pm) + fm; return car.calcOutput(*this, eg_cnt, true, lfo_am, phase); } // Operators used in the rhythm sounds generation process: // // Envelope Generator: // // channel operator register number Bass High Snare Tom Top // / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal // 6 / 0 12 50 70 90 f0 + // 6 / 1 15 53 73 93 f3 + // 7 / 0 13 51 71 91 f1 + // 7 / 1 16 54 74 94 f4 + // 8 / 0 14 52 72 92 f2 + // 8 / 1 17 55 75 95 f5 + // // Phase Generator: // // channel operator register number Bass High Snare Tom Top // / slot number MULTIPLE Drum Hat Drum Tom Cymbal // 6 / 0 12 30 + // 6 / 1 15 33 + // 7 / 0 13 31 + + + // 7 / 1 16 34 ----- n o t u s e d ----- // 8 / 0 14 32 + // 8 / 1 17 35 + + // // channel operator register number Bass High Snare Tom Top // number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal // 6 12,15 B6 A6 + // 7 13,16 B7 A7 + + + // 8 14,17 B8 A8 + + + // Phase generation is based on: // HH (13) channel 7->slot 1 combined with channel 8->slot 2 // (same combination as TOP CYMBAL but different output phases) // SD (16) channel 7->slot 1 // TOM (14) channel 8->slot 1 // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 // (same combination as HIGH HAT but different output phases) static inline int genPhaseHighHat(int phaseM7, int phaseC8, int noise_rng) { // hi == phase >= 0x200 bool hi; // enable gate based on frequency of operator 2 in channel 8 if (phaseC8 & 0x28) { hi = true; } else { // base frequency derived from operator 1 in channel 7 // VC++ requires explicit conversion to bool. Compiler bug?? const bool bit7 = (phaseM7 & 0x80) != 0; const bool bit3 = (phaseM7 & 0x08) != 0; const bool bit2 = (phaseM7 & 0x04) != 0; hi = (bit2 ^ bit7) | bit3; } if (noise_rng & 1) { return hi ? (0x200 | 0xD0) : (0xD0 >> 2); } else { return hi ? (0x200 | (0xD0 >> 2)) : 0xD0; } } static inline int genPhaseSnare(int phaseM7, int noise_rng) { // base frequency derived from operator 1 in channel 7 // noise bit XOR'es phase by 0x100 return ((phaseM7 & 0x100) + 0x100) ^ ((noise_rng & 1) << 8); } static inline int genPhaseCymbal(int phaseM7, int phaseC8) { // enable gate based on frequency of operator 2 in channel 8 if (phaseC8 & 0x28) { return 0x300; } else { // base frequency derived from operator 1 in channel 7 // VC++ requires explicit conversion to bool. Compiler bug?? const bool bit7 = (phaseM7 & 0x80) != 0; const bool bit3 = (phaseM7 & 0x08) != 0; const bool bit2 = (phaseM7 & 0x04) != 0; return ((bit2 != bit7) || bit3) ? 0x300 : 0x100; } } static void initTables() { static bool alreadyInit = false; if (alreadyInit) return; alreadyInit = true; for (int x = 0; x < TL_RES_LEN; ++x) { double m = (1 << 16) / pow(2, (x + 1) * (ENV_STEP / 4.0) / 8.0); m = floor(m); // we never reach (1 << 16) here due to the (x + 1) // result fits within 16 bits at maximum int n = int(m); // 16 bits here n >>= 4; // 12 bits here n = (n >> 1) + (n & 1); // round to nearest // 11 bits here (rounded) for (int i = 0; i < 11; ++i) { tl_tab[x * 2 + 0 + i * 2 * TL_RES_LEN] = n >> i; tl_tab[x * 2 + 1 + i * 2 * TL_RES_LEN] = -(n >> i); } } unsigned* full = &sin_tab[0 * SIN_LEN]; // waveform 0: standard sinus unsigned* half = &sin_tab[1 * SIN_LEN]; // waveform 1: positive part of sinus for (int i = 0; i < SIN_LEN / 4; ++i) { // checked on real hardware, see also // http://docs.google.com/Doc?id=dd8kqn9f_13cqjkf4gp double m = sin(((i * 2) + 1) * M_PI / SIN_LEN); int n = int(round(log(m) * (-256.0 / log(2.0)))); full[i] = half[i] = 2 * n; } for (int i = 0; i < SIN_LEN / 4; ++i) { full[SIN_LEN / 4 + i] = half[SIN_LEN / 4 + i] = full[SIN_LEN / 4 - 1 - i]; } for (int i = 0; i < SIN_LEN / 2; ++i) { full[SIN_LEN / 2 + i] = full[i] | 1; half[SIN_LEN / 2 + i] = TL_TAB_LEN; } } Slot::Slot() : phase(0), freq(0) { ar = dr = rr = KSR = ksl = mul = 0; fb_shift = op1_out[0] = op1_out[1] = 0; TL = TLL = egout = sl = 0; eg_sh_dp = eg_sh_ar = eg_sh_dr = eg_sh_rr = eg_sh_rs = 0; eg_sel_dp = eg_sel_ar = eg_sel_dr = eg_sel_rr = eg_sel_rs = eg_inc[0]; eg_mask_dp = eg_mask_ar = eg_mask_dr = eg_mask_rr = eg_mask_rs = 0; eg_sustain = false; setEnvelopeState(EG_OFF); key = AMmask = vib = 0; wavetable = &sin_tab[0 * SIN_LEN]; } void Slot::setKeyOn(KeyPart part) { if (!key) { // do NOT restart Phase Generator (verified on real YM2413) setEnvelopeState(EG_DUMP); } key |= part; } void Slot::setKeyOff(KeyPart part) { if (key) { key &= ~part; if (!key) { if (isActive()) { setEnvelopeState(EG_RELEASE); } } } } void Slot::setKeyOnOff(KeyPart part, bool enabled) { if (enabled) { setKeyOn(part); } else { setKeyOff(part); } } bool Slot::isActive() const { return state != EG_OFF; } void Slot::setEnvelopeState(EnvelopeState state_) { state = state_; } void Slot::setFrequencyMultiplier(byte value) { mul = mul_tab[value]; } void Slot::setKeyScaleRate(bool value) { KSR = value ? 0 : 2; } void Slot::setEnvelopeSustained(bool value) { eg_sustain = value; } void Slot::setVibrato(bool value) { vib = value; } void Slot::setAmplitudeModulation(bool value) { AMmask = value ? ~0 : 0; } void Slot::setTotalLevel(Channel& channel, byte value) { TL = value << (ENV_BITS - 2 - 7); // 7 bits TL (bit 6 = always 0) updateTotalLevel(channel); } void Slot::setKeyScaleLevel(Channel& channel, byte value) { ksl = value ? (3 - value) : 31; updateTotalLevel(channel); } void Slot::setWaveform(byte value) { wavetable = &sin_tab[value * SIN_LEN]; } void Slot::setFeedbackShift(byte value) { fb_shift = value ? 8 - value : 0; } void Slot::setAttackRate(const Channel& channel, byte value) { int kcodeScaled = channel.getKeyCode() >> KSR; ar = value ? 16 + (value << 2) : 0; updateAttackRate(kcodeScaled); } void Slot::setDecayRate(const Channel& channel, byte value) { int kcodeScaled = channel.getKeyCode() >> KSR; dr = value ? 16 + (value << 2) : 0; updateDecayRate(kcodeScaled); } void Slot::setReleaseRate(const Channel& channel, byte value) { int kcodeScaled = channel.getKeyCode() >> KSR; rr = value ? 16 + (value << 2) : 0; updateReleaseRate(kcodeScaled); } void Slot::setSustainLevel(byte value) { sl = sl_tab[value]; } void Slot::updateFrequency(Channel& channel) { updateTotalLevel(channel); updateGenerators(channel); } void Slot::resetOperators() { wavetable = &sin_tab[0 * SIN_LEN]; setEnvelopeState(EG_OFF); egout = MAX_ATT_INDEX; } void Slot::updateGenerators(Channel& channel) { // (frequency) phase increment counter freq = channel.getFrequencyIncrement() * mul; // calculate envelope generator rates const int kcodeScaled = channel.getKeyCode() >> KSR; updateAttackRate(kcodeScaled); updateDecayRate(kcodeScaled); updateReleaseRate(kcodeScaled); const int rs = channel.isSustained() ? 16 + (5 << 2) : 16 + (7 << 2); eg_sh_rs = eg_rate_shift[rs + kcodeScaled]; eg_sel_rs = eg_inc[eg_rate_select[rs + kcodeScaled]]; const int dp = 16 + (13 << 2); eg_sh_dp = eg_rate_shift[dp + kcodeScaled]; eg_sel_dp = eg_inc[eg_rate_select[dp + kcodeScaled]]; eg_mask_rs = (1 << eg_sh_rs) - 1; eg_mask_dp = (1 << eg_sh_dp) - 1; } Channel::Channel() : fc(0) { block_fnum = ksl_base = 0; sus = false; } void Channel::setFrequency(int block_fnum_) { if (block_fnum == block_fnum_) return; block_fnum = block_fnum_; ksl_base = ksl_tab[block_fnum >> 5]; fc = fnumToIncrement(block_fnum * 2); // Refresh Total Level and frequency counter in both SLOTs of this channel. mod.updateFrequency(*this); car.updateFrequency(*this); } void Channel::setFrequencyLow(byte value) { setFrequency((block_fnum & 0x0F00) | value); } void Channel::setFrequencyHigh(byte value) { setFrequency((value << 8) | (block_fnum & 0x00FF)); } int Channel::getBlockFNum() const { return block_fnum; } FreqIndex Channel::getFrequencyIncrement() const { return fc; } int Channel::getKeyScaleLevelBase() const { return ksl_base; } byte Channel::getKeyCode() const { // BLK 2,1,0 bits -> bits 3,2,1 of kcode, FNUM MSB -> kcode LSB return (block_fnum & 0x0F00) >> 8; } bool Channel::isSustained() const { return sus; } void Channel::setSustain(bool sustained) { sus = sustained; } void Channel::updateInstrumentPart(int part, byte value) { switch (part) { case 0: mod.setFrequencyMultiplier(value & 0x0F); mod.setKeyScaleRate((value & 0x10) != 0); mod.setEnvelopeSustained((value & 0x20) != 0); mod.setVibrato((value & 0x40) != 0); mod.setAmplitudeModulation((value & 0x80) != 0); mod.updateGenerators(*this); break; case 1: car.setFrequencyMultiplier(value & 0x0F); car.setKeyScaleRate((value & 0x10) != 0); car.setEnvelopeSustained((value & 0x20) != 0); car.setVibrato((value & 0x40) != 0); car.setAmplitudeModulation((value & 0x80) != 0); car.updateGenerators(*this); break; case 2: mod.setKeyScaleLevel(*this, value >> 6); mod.setTotalLevel(*this, value & 0x3F); break; case 3: mod.setWaveform((value & 0x08) >> 3); mod.setFeedbackShift(value & 0x07); car.setKeyScaleLevel(*this, value >> 6); car.setWaveform((value & 0x10) >> 4); break; case 4: mod.setAttackRate(*this, value >> 4); mod.setDecayRate(*this, value & 0x0F); break; case 5: car.setAttackRate(*this, value >> 4); car.setDecayRate(*this, value & 0x0F); break; case 6: mod.setSustainLevel(value >> 4); mod.setReleaseRate(*this, value & 0x0F); break; case 7: car.setSustainLevel(value >> 4); car.setReleaseRate(*this, value & 0x0F); break; } } void Channel::updateInstrument(const byte* inst) { for (int part = 0; part < 8; ++part) { updateInstrumentPart(part, inst[part]); } } YM2413::YM2413() : lfo_am_cnt(0), lfo_pm_cnt(0) { initTables(); memset(reg, 0, sizeof(reg)); // avoid UMR eg_cnt = 0; noise_rng = 0; reset(); } void YM2413::updateCustomInstrument(int part, byte value) { // Update instrument definition. inst_tab[0][part] = value; // Update every channel that has instrument 0 selected. const int numMelodicChannels = isRhythm() ? 6 : 9; for (int ch = 0; ch < numMelodicChannels; ++ch) { Channel& channel = channels[ch]; if ((reg[0x30 + ch] & 0xF0) == 0) { channel.updateInstrumentPart(part, value); } } } void YM2413::setRhythmFlags(byte old) { Channel& ch6 = channels[6]; Channel& ch7 = channels[7]; Channel& ch8 = channels[8]; // flags = X | X | mode | BD | SD | TOM | TC | HH byte flags = reg[0x0E]; if ((flags ^ old) & 0x20) { if (flags & 0x20) { // OFF -> ON // Bass drum. ch6.updateInstrument(inst_tab[16]); // High hat and snare drum. ch7.updateInstrument(inst_tab[17]); ch7.mod.setTotalLevel(ch7, (reg[0x37] >> 4) << 2); // High hat // Tom-tom and top cymbal. ch8.updateInstrument(inst_tab[18]); ch8.mod.setTotalLevel(ch8, (reg[0x38] >> 4) << 2); // Tom-tom } else { // ON -> OFF ch6.updateInstrument(inst_tab[reg[0x36] >> 4]); ch7.updateInstrument(inst_tab[reg[0x37] >> 4]); ch8.updateInstrument(inst_tab[reg[0x38] >> 4]); // BD key off ch6.mod.setKeyOff(Slot::KEY_RHYTHM); ch6.car.setKeyOff(Slot::KEY_RHYTHM); // HH key off ch7.mod.setKeyOff(Slot::KEY_RHYTHM); // SD key off ch7.car.setKeyOff(Slot::KEY_RHYTHM); // TOM key off ch8.mod.setKeyOff(Slot::KEY_RHYTHM); // TOP-CY off ch8.car.setKeyOff(Slot::KEY_RHYTHM); } } if (flags & 0x20) { // BD key on/off ch6.mod.setKeyOnOff(Slot::KEY_RHYTHM, (flags & 0x10) != 0); ch6.car.setKeyOnOff(Slot::KEY_RHYTHM, (flags & 0x10) != 0); // HH key on/off ch7.mod.setKeyOnOff(Slot::KEY_RHYTHM, (flags & 0x01) != 0); // SD key on/off ch7.car.setKeyOnOff(Slot::KEY_RHYTHM, (flags & 0x08) != 0); // TOM key on/off ch8.mod.setKeyOnOff(Slot::KEY_RHYTHM, (flags & 0x04) != 0); // TOP-CY key on/off ch8.car.setKeyOnOff(Slot::KEY_RHYTHM, (flags & 0x02) != 0); } } void YM2413::reset() { eg_cnt = 0; noise_rng = 1; // noise shift register idleSamples = 0; // setup instruments table for (int instrument = 0; instrument < 19; ++instrument) { for (int part = 0; part < 8; ++part) { inst_tab[instrument][part] = table[instrument][part]; } } // reset with register write writeReg(0x0F, 0); // test reg for (int i = 0x3F; i >= 0x10; --i) { writeReg(i, 0); } resetOperators(); } void YM2413::resetOperators() { for (int ch = 0; ch < 9; ++ch) { channels[ch].mod.resetOperators(); channels[ch].car.resetOperators(); } } bool YM2413::isRhythm() const { return (reg[0x0E] & 0x20) != 0; } Channel& YM2413::getChannelForReg(byte reg) { byte chan = (reg & 0x0F) % 9; // verified on real YM2413 return channels[chan]; } int YM2413::getAmplificationFactor() const { return 1 << 4; } void YM2413::generateChannels(int* bufs[9 + 5], unsigned num) { // TODO make channelActiveBits a member and // keep it up-to-date all the time // bits 0-8 -> ch[0-8].car // bits 9-17 -> ch[0-8].mod (only ch7 and ch8 used) unsigned channelActiveBits = 0; const int numMelodicChannels = isRhythm() ? 6 : 9; for (int ch = 0; ch < numMelodicChannels; ++ch) { if (channels[ch].car.isActive()) { channelActiveBits |= 1 << ch; } else { bufs[ch] = nullptr; } } if (isRhythm()) { bufs[6] = nullptr; bufs[7] = nullptr; bufs[8] = nullptr; for (int ch = 6; ch < 9; ++ch) { if (channels[ch].car.isActive()) { channelActiveBits |= 1 << ch; } else { bufs[ch + 3] = nullptr; } } if (channels[7].mod.isActive()) { channelActiveBits |= 1 << (7 + 9); } else { bufs[12] = nullptr; } if (channels[8].mod.isActive()) { channelActiveBits |= 1 << (8 + 9); } else { bufs[13] = nullptr; } } else { bufs[ 9] = nullptr; bufs[10] = nullptr; bufs[11] = nullptr; bufs[12] = nullptr; bufs[13] = nullptr; } if (channelActiveBits) { idleSamples = 0; } else { if (idleSamples > (CLOCK_FREQ / (72 * 5))) { // Optimization: // idle for over 1/5s = 200ms // we don't care that noise / AM / PM isn't exactly // in sync with the real HW when music resumes // Alternative: // implement an efficient advance(n) method return; } idleSamples += num; } for (unsigned i = 0; i < num; ++i) { // Amplitude modulation: 27 output levels (triangle waveform) // 1 level takes one of: 192, 256 or 448 samples // One entry from LFO_AM_TABLE lasts for 64 samples lfo_am_cnt.addQuantum(); if (lfo_am_cnt == LFOAMIndex(LFO_AM_TAB_ELEMENTS)) { // lfo_am_table is 210 elements long lfo_am_cnt = LFOAMIndex(0); } unsigned lfo_am = lfo_am_table[lfo_am_cnt.toInt()] >> 1; unsigned lfo_pm = lfo_pm_cnt.toInt() & 7; for (int ch = 0; ch < numMelodicChannels; ++ch) { Channel& channel = channels[ch]; int fm = channel.mod.calc_slot_mod(channel, eg_cnt, false, lfo_pm, lfo_am); if ((channelActiveBits >> ch) & 1) { bufs[ch][i] += channel.calcOutput(eg_cnt, lfo_pm, lfo_am, fm); } } if (isRhythm()) { // Bass Drum (verified on real YM3812): // - depends on the channel 6 'connect' register: // when connect = 0 it works the same as in normal (non-rhythm) mode // (op1->op2->out) // when connect = 1 _only_ operator 2 is present on output (op2->out), // operator 1 is ignored // - output sample always is multiplied by 2 Channel& channel6 = channels[6]; int fm = channel6.mod.calc_slot_mod(channels[6], eg_cnt, true, lfo_pm, lfo_am); if (channelActiveBits & (1 << 6)) { bufs[ 9][i] += 2 * channel6.calcOutput(eg_cnt, lfo_pm, lfo_am, fm); } // TODO: Skip phase generation if output will 0 anyway. // Possible by passing phase generator as a template parameter to // calcOutput. /* phaseC7 */channels[7].car.calc_phase(channels[7], lfo_pm); int phaseM7 = channels[7].mod.calc_phase(channels[7], lfo_pm); int phaseC8 = channels[8].car.calc_phase(channels[8], lfo_pm); int phaseM8 = channels[8].mod.calc_phase(channels[8], lfo_pm); // Snare Drum (verified on real YM3812) if (channelActiveBits & (1 << 7)) { Slot& SLOT7_2 = channels[7].car; bufs[10][i] += 2 * SLOT7_2.calcOutput(channels[7], eg_cnt, true, lfo_am, genPhaseSnare(phaseM7, noise_rng)); } // Top Cymbal (verified on real YM2413) if (channelActiveBits & (1 << 8)) { Slot& SLOT8_2 = channels[8].car; bufs[11][i] += 2 * SLOT8_2.calcOutput(channels[8], eg_cnt, true, lfo_am, genPhaseCymbal(phaseM7, phaseC8)); } // High Hat (verified on real YM3812) if (channelActiveBits & (1 << (7 + 9))) { Slot& SLOT7_1 = channels[7].mod; bufs[12][i] += 2 * SLOT7_1.calcOutput(channels[7], eg_cnt, true, lfo_am, genPhaseHighHat(phaseM7, phaseC8, noise_rng)); } // Tom Tom (verified on real YM3812) if (channelActiveBits & (1 << (8 + 9))) { Slot& SLOT8_1 = channels[8].mod; bufs[13][i] += 2 * SLOT8_1.calcOutput(channels[8], eg_cnt, true, lfo_am, phaseM8); } } // Vibrato: 8 output levels (triangle waveform) // 1 level takes 1024 samples lfo_pm_cnt.addQuantum(); ++eg_cnt; // The Noise Generator of the YM3812 is 23-bit shift register. // Period is equal to 2^23-2 samples. // Register works at sampling frequency of the chip, so output // can change on every sample. // // Output of the register and input to the bit 22 is: // bit0 XOR bit14 XOR bit15 XOR bit22 // // Simply use bit 22 as the noise output. // int j = ((noise_rng >> 0) ^ (noise_rng >> 14) ^ // (noise_rng >> 15) ^ (noise_rng >> 22)) & 1; // noise_rng = (j << 22) | (noise_rng >> 1); // // Instead of doing all the logic operations above, we // use a trick here (and use bit 0 as the noise output). // The difference is only that the noise bit changes one // step ahead. This doesn't matter since we don't know // what is real state of the noise_rng after the reset. if (noise_rng & 1) { noise_rng ^= 0x800302; } noise_rng >>= 1; } } void YM2413::writeReg(byte r, byte v) { byte old = reg[r]; reg[r] = v; switch (r & 0xF0) { case 0x00: { // 00-0F: control switch (r & 0x0F) { case 0x00: // AM/VIB/EGTYP/KSR/MULTI (modulator) case 0x01: // AM/VIB/EGTYP/KSR/MULTI (carrier) case 0x02: // Key Scale Level, Total Level (modulator) case 0x03: // Key Scale Level, carrier waveform, modulator waveform, // Feedback case 0x04: // Attack, Decay (modulator) case 0x05: // Attack, Decay (carrier) case 0x06: // Sustain, Release (modulator) case 0x07: // Sustain, Release (carrier) updateCustomInstrument(r, v); break; case 0x0E: setRhythmFlags(old); break; } break; } case 0x10: { // 10-18: FNUM 0-7 Channel& ch = getChannelForReg(r); ch.setFrequencyLow(v); break; } case 0x20: { // 20-28: suson, keyon, block, FNUM 8 Channel& ch = getChannelForReg(r); ch.mod.setKeyOnOff(Slot::KEY_MAIN, (v & 0x10) != 0); ch.car.setKeyOnOff(Slot::KEY_MAIN, (v & 0x10) != 0); ch.setSustain((v & 0x20) != 0); // Note: When changing the frequency, a new value for RS is // computed using the sustain value, so make sure the new // sustain value is committed first. ch.setFrequencyHigh(v & 0x0F); break; } case 0x30: { // inst 4 MSBs, VOL 4 LSBs Channel& ch = getChannelForReg(r); ch.car.setTotalLevel(ch, (v & 0x0F) << 2); // Check wether we are in rhythm mode and handle instrument/volume // register accordingly. byte chan = (r & 0x0F) % 9; // verified on real YM2413 if (isRhythm() && (chan >= 6)) { if (chan > 6) { // channel 7 or 8 in ryhthm mode // modulator envelope is HH(chan=7) or TOM(chan=8). ch.mod.setTotalLevel(ch, (v >> 4) << 2); } } else { if ((old & 0xF0) != (v & 0xF0)) { ch.updateInstrument(inst_tab[v >> 4]); } } break; } default: break; } } byte YM2413::peekReg(byte r) const { return reg[r]; } } // namespace Burczynsk static enum_string envelopeStateInfo[] = { { "DUMP", YM2413Burczynski::Slot::EG_DUMP }, { "ATTACK", YM2413Burczynski::Slot::EG_ATTACK }, { "DECAY", YM2413Burczynski::Slot::EG_DECAY }, { "SUSTAIN", YM2413Burczynski::Slot::EG_SUSTAIN }, { "RELEASE", YM2413Burczynski::Slot::EG_RELEASE }, { "OFF", YM2413Burczynski::Slot::EG_OFF } }; SERIALIZE_ENUM(YM2413Burczynski::Slot::EnvelopeState, envelopeStateInfo); namespace YM2413Burczynski { // version 1: initial version // version 2: - removed kcodeScaled // - calculated more members from other state // (TLL, freq, eg_sel_*, eg_sh_*) template void Slot::serialize(Archive& ar, unsigned /*version*/) { // TODO some of the serialized members here could be calculated from // other members int waveform = (wavetable == &sin_tab[0]) ? 0 : 1; ar.serialize("waveform", waveform); if (ar.isLoader()) { setWaveform(waveform); } ar.serialize("phase", phase); ar.serialize("TL", TL); ar.serialize("volume", egout); ar.serialize("sl", sl); ar.serialize("state", state); ar.serialize("op1_out", op1_out); ar.serialize("eg_sustain", eg_sustain); ar.serialize("fb_shift", fb_shift); ar.serialize("key", key); ar.serialize("ar", this->ar); ar.serialize("dr", dr); ar.serialize("rr", rr); ar.serialize("KSR", KSR); ar.serialize("ksl", ksl); ar.serialize("mul", mul); ar.serialize("AMmask", AMmask); ar.serialize("vib", vib); // These are calculated by updateTotalLevel() // TLL // These are calculated by updateGenerators() // freq, eg_sh_ar, eg_sel_ar, eg_sh_dr, eg_sel_dr, eg_sh_rr, eg_sel_rr // eg_sh_rs, eg_sel_rs, eg_sh_dp, eg_sel_dp } // version 1: original version // version 2: removed kcode // version 3: removed instvol_r template void Channel::serialize(Archive& ar, unsigned /*version*/) { // mod/car were originally an array, keep serializing as such for bwc Slot slots[2] = { mod, car }; ar.serialize("slots", slots); if (ar.isLoader()) { mod = slots[0]; car = slots[1]; } ar.serialize("block_fnum", block_fnum); ar.serialize("fc", fc); ar.serialize("ksl_base", ksl_base); ar.serialize("sus", sus); if (ar.isLoader()) { mod.updateFrequency(*this); car.updateFrequency(*this); } } // version 1: initial version // version 2: 'registers' are moved here (no longer serialized in base class) // version 3: removed 'rhythm' variable template void YM2413::serialize(Archive& ar, unsigned version) { if (ar.versionBelow(version, 2)) ar.beginTag("YM2413Core"); ar.serialize("registers", reg); if (ar.versionBelow(version, 2)) ar.endTag("YM2413Core"); // only serialize user instrument ar.serialize_blob("user_instrument", inst_tab[0], 8); ar.serialize("channels", channels); ar.serialize("eg_cnt", eg_cnt); ar.serialize("noise_rng", noise_rng); ar.serialize("lfo_am_cnt", lfo_am_cnt); ar.serialize("lfo_pm_cnt", lfo_pm_cnt); // don't serialize idleSamples, it's only an optimization } } // namespace Burczynsk using YM2413Burczynski::YM2413; INSTANTIATE_SERIALIZE_METHODS(YM2413); REGISTER_POLYMORPHIC_INITIALIZER(YM2413Core, YM2413, "YM2413-Jarek-Burczynski"); } // namespace openmsx openmsx-0.10.0/src/sound/YMF262.cc0000644000175000017500000015756512262345041017202 0ustar manuelmanuel00000000000000/* * * File: ymf262.c - software implementation of YMF262 * FM sound generator type OPL3 * * Copyright (C) 2003 Jarek Burczynski * * Version 0.2 * * * Revision History: * * 03-03-2003: initial release * - thanks to Olivier Galibert and Chris Hardy for YMF262 and YAC512 chips * - thanks to Stiletto for the datasheets * * * * differences between OPL2 and OPL3 not documented in Yamaha datahasheets: * - sinus table is a little different: the negative part is off by one... * * - in order to enable selection of four different waveforms on OPL2 * one must set bit 5 in register 0x01(test). * on OPL3 this bit is ignored and 4-waveform select works *always*. * (Don't confuse this with OPL3's 8-waveform select.) * * - Envelope Generator: all 15 x rates take zero time on OPL3 * (on OPL2 15 0 and 15 1 rates take some time while 15 2 and 15 3 rates * take zero time) * * - channel calculations: output of operator 1 is in perfect sync with * output of operator 2 on OPL3; on OPL and OPL2 output of operator 1 * is always delayed by one sample compared to output of operator 2 * * * differences between OPL2 and OPL3 shown in datasheets: * - YMF262 does not support CSM mode */ #include "YMF262.hh" #include "ResampledSoundDevice.hh" #include "EmuTimer.hh" #include "IRQHelper.hh" #include "FixedPoint.hh" #include "SimpleDebuggable.hh" #include "DeviceConfig.hh" #include "MSXMotherBoard.hh" #include "serialize.hh" #include "memory.hh" #include #include namespace openmsx { class YMF262Debuggable : public SimpleDebuggable { public: YMF262Debuggable(MSXMotherBoard& motherBoard, YMF262& ymf262, const std::string& name); virtual byte read(unsigned address); virtual void write(unsigned address, byte value, EmuTime::param time); private: YMF262& ymf262; }; enum EnvelopeState { EG_ATTACK, EG_DECAY, EG_SUSTAIN, EG_RELEASE, EG_OFF }; class YMF262Channel; /** 16.16 fixed point type for frequency calculations */ typedef FixedPoint<16> FreqIndex; static inline FreqIndex fnumToIncrement(unsigned block_fnum) { // opn phase increment counter = 20bit // chip works with 10.10 fixed point, while we use 16.16 unsigned block = (block_fnum & 0x1C00) >> 10; return FreqIndex(block_fnum & 0x03FF) >> (11 - block); } class YMF262Slot { public: YMF262Slot(); inline int op_calc(unsigned phase, unsigned lfo_am) const; inline void FM_KEYON(byte key_set); inline void FM_KEYOFF(byte key_clr); inline void advanceEnvelopeGenerator(unsigned eg_cnt); inline void advancePhaseGenerator(YMF262Channel& ch, unsigned lfo_pm); void update_ar_dr(); void update_rr(); void calc_fc(const YMF262Channel& ch); /** Sets the amount of feedback [0..7] */ void setFeedbackShift(byte value) { fb_shift = value ? 9 - value : 0; } template void serialize(Archive& ar, unsigned version); // Phase Generator FreqIndex Cnt; // frequency counter FreqIndex Incr; // frequency counter step int* connect; // slot output pointer int op1_out[2]; // slot1 output for feedback // Envelope Generator unsigned TL; // total level: TL << 2 int TLL; // adjusted now TL int volume; // envelope counter int sl; // sustain level: sl_tab[SL] unsigned* wavetable; // waveform select EnvelopeState state; // EG: phase type unsigned eg_m_ar;// (attack state) unsigned eg_m_dr;// (decay state) unsigned eg_m_rr;// (release state) byte eg_sh_ar; // (attack state) byte eg_sel_ar; // (attack state) byte eg_sh_dr; // (decay state) byte eg_sel_dr; // (decay state) byte eg_sh_rr; // (release state) byte eg_sel_rr; // (release state) byte key; // 0 = KEY OFF, >0 = KEY ON byte fb_shift; // PG: feedback shift value bool CON; // PG: connection (algorithm) type bool eg_type; // EG: percussive/non-percussive mode // LFO byte AMmask; // LFO Amplitude Modulation enable mask bool vib; // LFO Phase Modulation enable flag (active high) byte ar; // attack rate: AR<<2 byte dr; // decay rate: DR<<2 byte rr; // release rate:RR<<2 byte KSR; // key scale rate byte ksl; // keyscale level byte ksr; // key scale rate: kcode>>KSR byte mul; // multiple: mul_tab[ML] }; class YMF262Channel { public: YMF262Channel(); void chan_calc(unsigned lfo_am); void chan_calc_ext(unsigned lfo_am); template void serialize(Archive& ar, unsigned version); YMF262Slot slot[2]; int block_fnum; // block+fnum FreqIndex fc; // Freq. Increment base int ksl_base; // KeyScaleLevel Base step byte kcode; // key code (for key scaling) // there are 12 2-operator channels which can be combined in pairs // to form six 4-operator channel, they are: // 0 and 3, // 1 and 4, // 2 and 5, // 9 and 12, // 10 and 13, // 11 and 14 bool extended; // set if this channel forms up a 4op channel with // another channel (only used by first of pair of // channels, ie 0,1,2 and 9,10,11) }; class YMF262::Impl : private ResampledSoundDevice, private EmuTimerCallback { public: Impl(YMF262& self, const std::string& name, const DeviceConfig& config, bool isYMF278); virtual ~Impl(); void reset(EmuTime::param time); void writeReg (unsigned r, byte v, EmuTime::param time); void writeReg512(unsigned r, byte v, EmuTime::param time); byte readReg(unsigned reg); byte peekReg(unsigned reg) const; byte readStatus(); byte peekStatus() const; template void serialize(Archive& ar, unsigned version); private: // SoundDevice virtual int getAmplificationFactor() const; virtual void generateChannels(int** bufs, unsigned num); void callback(byte flag); void writeRegDirect(unsigned r, byte v, EmuTime::param time); void init_tables(); void setStatus(byte flag); void resetStatus(byte flag); void changeStatusMask(byte flag); void advance(); inline int genPhaseHighHat(); inline int genPhaseSnare(); inline int genPhaseCymbal(); void chan_calc_rhythm(unsigned lfo_am); void set_mul(unsigned sl, byte v); void set_ksl_tl(unsigned sl, byte v); void set_ar_dr(unsigned sl, byte v); void set_sl_rr(unsigned sl, byte v); bool checkMuteHelper(); inline bool isExtended(unsigned ch) const; inline YMF262Channel& getFirstOfPair(unsigned ch); inline YMF262Channel& getSecondOfPair(unsigned ch); const std::unique_ptr debuggable; // Bitmask for register 0x04 static const int R04_ST1 = 0x01; // Timer1 Start static const int R04_ST2 = 0x02; // Timer2 Start static const int R04_MASK_T2 = 0x20; // Mask Timer2 flag static const int R04_MASK_T1 = 0x40; // Mask Timer1 flag static const int R04_IRQ_RESET = 0x80; // IRQ RESET // Bitmask for status register static const int STATUS_T2 = R04_MASK_T2; static const int STATUS_T1 = R04_MASK_T1; // Timers (see EmuTimer class for details about timing) const std::unique_ptr timer1; // 80.8us OPL4 ( 80.5us OPL3) const std::unique_ptr timer2; // 323.1us OPL4 (321.8us OPL3) IRQHelper irq; int chanout[18]; // 18 channels byte reg[512]; YMF262Channel channel[18]; // OPL3 chips have 18 channels unsigned pan[18 * 4]; // channels output masks 4 per channel // 0xffffffff = enable unsigned eg_cnt; // global envelope generator counter unsigned noise_rng; // 23 bit noise shift register // LFO typedef FixedPoint< 6> LFOAMIndex; typedef FixedPoint<10> LFOPMIndex; LFOAMIndex lfo_am_cnt; LFOPMIndex lfo_pm_cnt; bool lfo_am_depth; byte lfo_pm_depth_range; byte rhythm; // Rhythm mode bool nts; // NTS (note select) bool OPL3_mode; // OPL3 extension enable flag byte status; // status flag byte status2; byte statusMask; // status mask bool alreadySignaledNEW2; const bool isYMF278; // true iff this is actually a YMF278 // ATM only used for NEW2 bit }; // envelope output entries static const int ENV_BITS = 10; static const int ENV_LEN = 1 << ENV_BITS; static const double ENV_STEP = 128.0 / ENV_LEN; static const int MAX_ATT_INDEX = (1 << (ENV_BITS - 1)) - 1; // 511 static const int MIN_ATT_INDEX = 0; // sinwave entries static const int SIN_BITS = 10; static const int SIN_LEN = 1 << SIN_BITS; static const int SIN_MASK = SIN_LEN - 1; static const int TL_RES_LEN = 256; // 8 bits addressing (real chip) // register number to channel number , slot offset static const byte MOD = 0; static const byte CAR = 1; // mapping of register number (offset) to slot number used by the emulator static const int slot_array[32] = { 0, 2, 4, 1, 3, 5, -1, -1, 6, 8, 10, 7, 9, 11, -1, -1, 12, 14, 16, 13, 15, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; // key scale level // table is 3dB/octave , DV converts this into 6dB/octave // 0.1875 is bit 0 weight of the envelope counter (volume) expressed // in the 'decibel' scale #define DV(x) int(x / (0.1875 / 2.0)) static const unsigned ksl_tab[8 * 16] = { // OCT 0 DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), // OCT 1 DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.750), DV( 1.125), DV( 1.500), DV( 1.875), DV( 2.250), DV( 2.625), DV( 3.000), // OCT 2 DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 0.000), DV( 1.125), DV( 1.875), DV( 2.625), DV( 3.000), DV( 3.750), DV( 4.125), DV( 4.500), DV( 4.875), DV( 5.250), DV( 5.625), DV( 6.000), // OCT 3 DV( 0.000), DV( 0.000), DV( 0.000), DV( 1.875), DV( 3.000), DV( 4.125), DV( 4.875), DV( 5.625), DV( 6.000), DV( 6.750), DV( 7.125), DV( 7.500), DV( 7.875), DV( 8.250), DV( 8.625), DV( 9.000), // OCT 4 DV( 0.000), DV( 0.000), DV( 3.000), DV( 4.875), DV( 6.000), DV( 7.125), DV( 7.875), DV( 8.625), DV( 9.000), DV( 9.750), DV(10.125), DV(10.500), DV(10.875), DV(11.250), DV(11.625), DV(12.000), // OCT 5 DV( 0.000), DV( 3.000), DV( 6.000), DV( 7.875), DV( 9.000), DV(10.125), DV(10.875), DV(11.625), DV(12.000), DV(12.750), DV(13.125), DV(13.500), DV(13.875), DV(14.250), DV(14.625), DV(15.000), // OCT 6 DV( 0.000), DV( 6.000), DV( 9.000), DV(10.875), DV(12.000), DV(13.125), DV(13.875), DV(14.625), DV(15.000), DV(15.750), DV(16.125), DV(16.500), DV(16.875), DV(17.250), DV(17.625), DV(18.000), // OCT 7 DV( 0.000), DV( 9.000), DV(12.000), DV(13.875), DV(15.000), DV(16.125), DV(16.875), DV(17.625), DV(18.000), DV(18.750), DV(19.125), DV(19.500), DV(19.875), DV(20.250), DV(20.625), DV(21.000) }; #undef DV // sustain level table (3dB per step) // 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB) #define SC(db) unsigned(db * (2.0 / ENV_STEP)) static const unsigned sl_tab[16] = { SC( 0), SC( 1), SC( 2), SC(3 ), SC(4 ), SC(5 ), SC(6 ), SC( 7), SC( 8), SC( 9), SC(10), SC(11), SC(12), SC(13), SC(14), SC(31) }; #undef SC static const byte RATE_STEPS = 8; static const byte eg_inc[15 * RATE_STEPS] = { //cycle:0 1 2 3 4 5 6 7 0,1, 0,1, 0,1, 0,1, // 0 rates 00..12 0 (increment by 0 or 1) 0,1, 0,1, 1,1, 0,1, // 1 rates 00..12 1 0,1, 1,1, 0,1, 1,1, // 2 rates 00..12 2 0,1, 1,1, 1,1, 1,1, // 3 rates 00..12 3 1,1, 1,1, 1,1, 1,1, // 4 rate 13 0 (increment by 1) 1,1, 1,2, 1,1, 1,2, // 5 rate 13 1 1,2, 1,2, 1,2, 1,2, // 6 rate 13 2 1,2, 2,2, 1,2, 2,2, // 7 rate 13 3 2,2, 2,2, 2,2, 2,2, // 8 rate 14 0 (increment by 2) 2,2, 2,4, 2,2, 2,4, // 9 rate 14 1 2,4, 2,4, 2,4, 2,4, // 10 rate 14 2 2,4, 4,4, 2,4, 4,4, // 11 rate 14 3 4,4, 4,4, 4,4, 4,4, // 12 rates 15 0, 15 1, 15 2, 15 3 for decay 8,8, 8,8, 8,8, 8,8, // 13 rates 15 0, 15 1, 15 2, 15 3 for attack (zero time) 0,0, 0,0, 0,0, 0,0, // 14 infinity rates for attack and decay(s) }; #define O(a) (a * RATE_STEPS) // note that there is no O(13) in this table - it's directly in the code static const byte eg_rate_select[16 + 64 + 16] = { // Envelope Generator rates (16 + 64 rates + 16 RKS) // 16 infinite time rates O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), O(14), // rates 00-12 O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), O( 0), O( 1), O( 2), O( 3), // rate 13 O( 4), O( 5), O( 6), O( 7), // rate 14 O( 8), O( 9), O(10), O(11), // rate 15 O(12), O(12), O(12), O(12), // 16 dummy rates (same as 15 3) O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), O(12), }; #undef O // rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 // shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 // mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 #define O(a) (a * 1) static const byte eg_rate_shift[16 + 64 + 16] = { // Envelope Generator counter shifts (16 + 64 rates + 16 RKS) // 16 infinite time rates O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), // rates 00-15 O(12), O(12), O(12), O(12), O(11), O(11), O(11), O(11), O(10), O(10), O(10), O(10), O( 9), O( 9), O( 9), O( 9), O( 8), O( 8), O( 8), O( 8), O( 7), O( 7), O( 7), O( 7), O( 6), O( 6), O( 6), O( 6), O( 5), O( 5), O( 5), O( 5), O( 4), O( 4), O( 4), O( 4), O( 3), O( 3), O( 3), O( 3), O( 2), O( 2), O( 2), O( 2), O( 1), O( 1), O( 1), O( 1), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), // 16 dummy rates (same as 15 3) O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), O( 0), }; #undef O // multiple table #define ML(x) byte(2 * x) static const byte mul_tab[16] = { // 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 ML( 0.5), ML( 1.0), ML( 2.0), ML( 3.0), ML( 4.0), ML( 5.0), ML( 6.0), ML( 7.0), ML( 8.0), ML( 9.0), ML(10.0), ML(10.0), ML(12.0), ML(12.0), ML(15.0), ML(15.0) }; #undef ML // TL_TAB_LEN is calculated as: // (12+1)=13 - sinus amplitude bits (Y axis) // additional 1: to compensate for calculations of negative part of waveform // (if we don't add it then the greatest possible _negative_ value would be -2 // and we really need -1 for waveform #7) // 2 - sinus sign bit (Y axis) // TL_RES_LEN - sinus resolution (X axis) static const int TL_TAB_LEN = 13 * 2 * TL_RES_LEN; static int tl_tab[TL_TAB_LEN]; static const int ENV_QUIET = TL_TAB_LEN >> 4; // sin waveform table in 'decibel' scale // there are eight waveforms on OPL3 chips static unsigned sin_tab[SIN_LEN * 8]; // LFO Amplitude Modulation table (verified on real YM3812) // 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples // // Length: 210 elements // // Each of the elements has to be repeated // exactly 64 times (on 64 consecutive samples). // The whole table takes: 64 * 210 = 13440 samples. // // When AM = 1 data is used directly // When AM = 0 data is divided by 4 before being used (loosing precision is important) static const unsigned LFO_AM_TAB_ELEMENTS = 210; static const byte lfo_am_table[LFO_AM_TAB_ELEMENTS] = { 0, 0, 0, /**/ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, /**/ 25, 25, 25, 25, 24, 24, 24, 24, 23, 23, 23, 23, 22, 22, 22, 22, 21, 21, 21, 21, 20, 20, 20, 20, 19, 19, 19, 19, 18, 18, 18, 18, 17, 17, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 13, 13, 13, 13, 12, 12, 12, 12, 11, 11, 11, 11, 10, 10, 10, 10, 9, 9, 9, 9, 8, 8, 8, 8, 7, 7, 7, 7, 6, 6, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 3, 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1 }; // LFO Phase Modulation table (verified on real YM3812) static const signed char lfo_pm_table[8 * 8 * 2] = { // FNUM2/FNUM = 00 0xxxxxxx (0x0000) 0, 0, 0, 0, 0, 0, 0, 0, // LFO PM depth = 0 0, 0, 0, 0, 0, 0, 0, 0, // LFO PM depth = 1 // FNUM2/FNUM = 00 1xxxxxxx (0x0080) 0, 0, 0, 0, 0, 0, 0, 0, // LFO PM depth = 0 1, 0, 0, 0,-1, 0, 0, 0, // LFO PM depth = 1 // FNUM2/FNUM = 01 0xxxxxxx (0x0100) 1, 0, 0, 0,-1, 0, 0, 0, // LFO PM depth = 0 2, 1, 0,-1,-2,-1, 0, 1, // LFO PM depth = 1 // FNUM2/FNUM = 01 1xxxxxxx (0x0180) 1, 0, 0, 0,-1, 0, 0, 0, // LFO PM depth = 0 3, 1, 0,-1,-3,-1, 0, 1, // LFO PM depth = 1 // FNUM2/FNUM = 10 0xxxxxxx (0x0200) 2, 1, 0,-1,-2,-1, 0, 1, // LFO PM depth = 0 4, 2, 0,-2,-4,-2, 0, 2, // LFO PM depth = 1 // FNUM2/FNUM = 10 1xxxxxxx (0x0280) 2, 1, 0,-1,-2,-1, 0, 1, // LFO PM depth = 0 5, 2, 0,-2,-5,-2, 0, 2, // LFO PM depth = 1 // FNUM2/FNUM = 11 0xxxxxxx (0x0300) 3, 1, 0,-1,-3,-1, 0, 1, // LFO PM depth = 0 6, 3, 0,-3,-6,-3, 0, 3, // LFO PM depth = 1 // FNUM2/FNUM = 11 1xxxxxxx (0x0380) 3, 1, 0,-1,-3,-1, 0, 1, // LFO PM depth = 0 7, 3, 0,-3,-7,-3, 0, 3 // LFO PM depth = 1 }; // TODO clean this up static int phase_modulation; // phase modulation input (SLOT 2) static int phase_modulation2; // phase modulation input (SLOT 3 // in 4 operator channels) YMF262Slot::YMF262Slot() : Cnt(0), Incr(0) { ar = dr = rr = KSR = ksl = ksr = mul = 0; fb_shift = op1_out[0] = op1_out[1] = 0; CON = eg_type = vib = false; connect = nullptr; TL = TLL = volume = sl = 0; state = EG_OFF; eg_m_ar = eg_sh_ar = eg_sel_ar = eg_m_dr = eg_sh_dr = 0; eg_sel_dr = eg_m_rr = eg_sh_rr = eg_sel_rr = 0; key = AMmask = 0; wavetable = &sin_tab[0 * SIN_LEN]; } YMF262Channel::YMF262Channel() { block_fnum = ksl_base = kcode = 0; extended = false; fc = FreqIndex(0); } void YMF262::Impl::callback(byte flag) { setStatus(flag); } // status set and IRQ handling void YMF262::Impl::setStatus(byte flag) { // set status flag masking out disabled IRQs status |= flag; if (status & statusMask) { status |= 0x80; irq.set(); } } // status reset and IRQ handling void YMF262::Impl::resetStatus(byte flag) { // reset status flag status &= ~flag; if (!(status & statusMask)) { status &= 0x7F; irq.reset(); } } // IRQ mask set void YMF262::Impl::changeStatusMask(byte flag) { statusMask = flag; status &= statusMask; if (status) { status |= 0x80; irq.set(); } else { status &= 0x7F; irq.reset(); } } void YMF262Slot::advanceEnvelopeGenerator(unsigned eg_cnt) { switch (state) { case EG_ATTACK: if (!(eg_cnt & eg_m_ar)) { volume += (~volume * eg_inc[eg_sel_ar + ((eg_cnt >> eg_sh_ar) & 7)]) >> 3; if (volume <= MIN_ATT_INDEX) { volume = MIN_ATT_INDEX; state = EG_DECAY; } } break; case EG_DECAY: if (!(eg_cnt & eg_m_dr)) { volume += eg_inc[eg_sel_dr + ((eg_cnt >> eg_sh_dr) & 7)]; if (volume >= sl) { state = EG_SUSTAIN; } } break; case EG_SUSTAIN: // this is important behaviour: // one can change percusive/non-percussive // modes on the fly and the chip will remain // in sustain phase - verified on real YM3812 if (eg_type) { // non-percussive mode // do nothing } else { // percussive mode // during sustain phase chip adds Release Rate (in percussive mode) if (!(eg_cnt & eg_m_rr)) { volume += eg_inc[eg_sel_rr + ((eg_cnt >> eg_sh_rr) & 7)]; if (volume >= MAX_ATT_INDEX) { volume = MAX_ATT_INDEX; } } else { // do nothing in sustain phase } } break; case EG_RELEASE: if (!(eg_cnt & eg_m_rr)) { volume += eg_inc[eg_sel_rr + ((eg_cnt >> eg_sh_rr) & 7)]; if (volume >= MAX_ATT_INDEX) { volume = MAX_ATT_INDEX; state = EG_OFF; } } break; default: break; } } void YMF262Slot::advancePhaseGenerator(YMF262Channel& ch, unsigned lfo_pm) { if (vib) { // LFO phase modulation active unsigned block_fnum = ch.block_fnum; unsigned fnum_lfo = (block_fnum & 0x0380) >> 7; int lfo_fn_table_index_offset = lfo_pm_table[lfo_pm + 16 * fnum_lfo]; Cnt += fnumToIncrement(block_fnum + lfo_fn_table_index_offset) * mul; } else { // LFO phase modulation disabled for this operator Cnt += Incr; } } // advance to next sample void YMF262::Impl::advance() { // Vibrato: 8 output levels (triangle waveform); // 1 level takes 1024 samples lfo_pm_cnt.addQuantum(); unsigned lfo_pm = (lfo_pm_cnt.toInt() & 7) | lfo_pm_depth_range; ++eg_cnt; for (int c = 0; c < 18; ++c) { YMF262Channel& ch = channel[c]; for (int s = 0; s < 2; ++s) { YMF262Slot& op = ch.slot[s]; op.advanceEnvelopeGenerator(eg_cnt); op.advancePhaseGenerator(ch, lfo_pm); } } // The Noise Generator of the YM3812 is 23-bit shift register. // Period is equal to 2^23-2 samples. // Register works at sampling frequency of the chip, so output // can change on every sample. // // Output of the register and input to the bit 22 is: // bit0 XOR bit14 XOR bit15 XOR bit22 // // Simply use bit 22 as the noise output. // // unsigned j = ((noise_rng >> 0) ^ (noise_rng >> 14) ^ // (noise_rng >> 15) ^ (noise_rng >> 22)) & 1; // noise_rng = (j << 22) | (noise_rng >> 1); // // Instead of doing all the logic operations above, we // use a trick here (and use bit 0 as the noise output). // The difference is only that the noise bit changes one // step ahead. This doesn't matter since we don't know // what is real state of the noise_rng after the reset. if (noise_rng & 1) { noise_rng ^= 0x800302; } noise_rng >>= 1; } inline int YMF262Slot::op_calc(unsigned phase, unsigned lfo_am) const { unsigned env = (TLL + volume + (lfo_am & AMmask)) << 4; int p = env + wavetable[phase & SIN_MASK]; return (p < TL_TAB_LEN) ? tl_tab[p] : 0; } // calculate output of a standard 2 operator channel // (or 1st part of a 4-op channel) void YMF262Channel::chan_calc(unsigned lfo_am) { // !! something is wrong with this, it caused bug // !! [2823673] moonsound 4 operator FM fail // !! optimization disabled for now // !! TODO investigate // !! maybe this micro optimization isn't worth the trouble/risk // !! // - mod.connect can point to 'phase_modulation' or 'ch0-output' // - car.connect can point to 'phase_modulation2' or 'ch0-output' // (see register #C0-#C8 writes) // - phase_modulation2 is only used in 4op mode // - mod.connect and car.connect can point to the same thing, so we need // an addition for car.connect (and initialize phase_modulation2 to // zero). For mod.connect we can directly assign the value. // ?? is this paragraph correct ?? // phase_modulation should be initialized to zero here. But there seems // to be an optimization bug in gcc-4.2: it *seems* that when we // initialize phase_modulation to zero in this function, the optimizer // assumes it still has value zero at the end of this function (where // it's used to calculate car.connect). As a workaround we initialize // phase_modulation each time before calling this function. phase_modulation = 0; phase_modulation2 = 0; YMF262Slot& mod = slot[MOD]; int out = mod.fb_shift ? mod.op1_out[0] + mod.op1_out[1] : 0; mod.op1_out[0] = mod.op1_out[1]; mod.op1_out[1] = mod.op_calc(mod.Cnt.toInt() + (out >> mod.fb_shift), lfo_am); *mod.connect += mod.op1_out[1]; YMF262Slot& car = slot[CAR]; *car.connect += car.op_calc(car.Cnt.toInt() + phase_modulation, lfo_am); } // calculate output of a 2nd part of 4-op channel void YMF262Channel::chan_calc_ext(unsigned lfo_am) { // !! see remark in chan_cal(), something is wrong with this // !! optimization disabled for now // !! // - mod.connect can point to 'phase_modulation' or 'ch3-output' // - car.connect always points to 'ch3-output' (always 4op-mode) // (see register #C0-#C8 writes) // - mod.connect and car.connect can point to the same thing, so we need // an addition for car.connect. For mod.connect we can directly assign // the value. phase_modulation = 0; YMF262Slot& mod = slot[MOD]; *mod.connect += mod.op_calc(mod.Cnt.toInt() + phase_modulation2, lfo_am); YMF262Slot& car = slot[CAR]; *car.connect += car.op_calc(car.Cnt.toInt() + phase_modulation, lfo_am); } // operators used in the rhythm sounds generation process: // // Envelope Generator: // // channel operator register number Bass High Snare Tom Top // / slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal // 6 / 0 12 50 70 90 f0 + // 6 / 1 15 53 73 93 f3 + // 7 / 0 13 51 71 91 f1 + // 7 / 1 16 54 74 94 f4 + // 8 / 0 14 52 72 92 f2 + // 8 / 1 17 55 75 95 f5 + // // Phase Generator: // // channel operator register number Bass High Snare Tom Top // / slot number MULTIPLE Drum Hat Drum Tom Cymbal // 6 / 0 12 30 + // 6 / 1 15 33 + // 7 / 0 13 31 + + + // 7 / 1 16 34 ----- n o t u s e d ----- // 8 / 0 14 32 + // 8 / 1 17 35 + + // // channel operator register number Bass High Snare Tom Top // number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal // 6 12,15 B6 A6 + // // 7 13,16 B7 A7 + + + // // 8 14,17 B8 A8 + + + // The following formulas can be well optimized. // I leave them in direct form for now (in case I've missed something). inline int YMF262::Impl::genPhaseHighHat() { // high hat phase generation (verified on real YM3812): // phase = d0 or 234 (based on frequency only) // phase = 34 or 2d0 (based on noise) // base frequency derived from operator 1 in channel 7 int op71phase = channel[7].slot[MOD].Cnt.toInt(); bool bit7 = (op71phase & 0x80) != 0; bool bit3 = (op71phase & 0x08) != 0; bool bit2 = (op71phase & 0x04) != 0; bool res1 = (bit2 ^ bit7) | bit3; // when res1 = 0 phase = 0x000 | 0xd0; // when res1 = 1 phase = 0x200 | (0xd0>>2); unsigned phase = res1 ? (0x200 | (0xd0 >> 2)) : 0xd0; // enable gate based on frequency of operator 2 in channel 8 int op82phase = channel[8].slot[CAR].Cnt.toInt(); bool bit5e= (op82phase & 0x20) != 0; bool bit3e= (op82phase & 0x08) != 0; bool res2 = (bit3e ^ bit5e); // when res2 = 0 pass the phase from calculation above (res1); // when res2 = 1 phase = 0x200 | (0xd0>>2); if (res2) { phase = (0x200 | (0xd0 >> 2)); } // when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 // when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change if (phase & 0x200) { if (noise_rng & 1) { phase = 0x200 | 0xd0; } } else { // when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 // when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change if (noise_rng & 1) { phase = 0xd0 >> 2; } } return phase; } inline int YMF262::Impl::genPhaseSnare() { // verified on real YM3812 // base frequency derived from operator 1 in channel 7 // noise bit XOR'es phase by 0x100 return ((channel[7].slot[MOD].Cnt.toInt() & 0x100) + 0x100) ^ ((noise_rng & 1) << 8); } inline int YMF262::Impl::genPhaseCymbal() { // verified on real YM3812 // enable gate based on frequency of operator 2 in channel 8 // NOTE: YM2413_2 uses bit5 | bit3, this core uses bit5 ^ bit3 // most likely only one of the two is correct int op82phase = channel[8].slot[CAR].Cnt.toInt(); if ((op82phase ^ (op82phase << 2)) & 0x20) { // bit5 ^ bit3 return 0x300; } else { // base frequency derived from operator 1 in channel 7 int op71phase = channel[7].slot[MOD].Cnt.toInt(); bool bit7 = (op71phase & 0x80) != 0; bool bit3 = (op71phase & 0x08) != 0; bool bit2 = (op71phase & 0x04) != 0; return ((bit2 != bit7) || bit3) ? 0x300 : 0x100; } } // calculate rhythm void YMF262::Impl::chan_calc_rhythm(unsigned lfo_am) { // Bass Drum (verified on real YM3812): // - depends on the channel 6 'connect' register: // when connect = 0 it works the same as in normal (non-rhythm) // mode (op1->op2->out) // when connect = 1 _only_ operator 2 is present on output // (op2->out), operator 1 is ignored // - output sample always is multiplied by 2 YMF262Slot& mod6 = channel[6].slot[MOD]; int out = mod6.fb_shift ? mod6.op1_out[0] + mod6.op1_out[1] : 0; mod6.op1_out[0] = mod6.op1_out[1]; int pm = mod6.CON ? 0 : mod6.op1_out[0]; mod6.op1_out[1] = mod6.op_calc(mod6.Cnt.toInt() + (out >> mod6.fb_shift), lfo_am); YMF262Slot& car6 = channel[6].slot[CAR]; chanout[6] += 2 * car6.op_calc(car6.Cnt.toInt() + pm, lfo_am); // Phase generation is based on: // HH (13) channel 7->slot 1 combined with channel 8->slot 2 // (same combination as TOP CYMBAL but different output phases) // SD (16) channel 7->slot 1 // TOM (14) channel 8->slot 1 // TOP (17) channel 7->slot 1 combined with channel 8->slot 2 // (same combination as HIGH HAT but different output phases) // // Envelope generation based on: // HH channel 7->slot1 // SD channel 7->slot2 // TOM channel 8->slot1 // TOP channel 8->slot2 YMF262Slot& mod7 = channel[7].slot[MOD]; chanout[7] += 2 * mod7.op_calc(genPhaseHighHat(), lfo_am); YMF262Slot& car7 = channel[7].slot[CAR]; chanout[7] += 2 * car7.op_calc(genPhaseSnare(), lfo_am); YMF262Slot& mod8 = channel[8].slot[MOD]; chanout[8] += 2 * mod8.op_calc(mod8.Cnt.toInt(), lfo_am); YMF262Slot& car8 = channel[8].slot[CAR]; chanout[8] += 2 * car8.op_calc(genPhaseCymbal(), lfo_am); } // generic table initialize void YMF262::Impl::init_tables() { static bool alreadyInit = false; if (alreadyInit) return; alreadyInit = true; for (int x = 0; x < TL_RES_LEN; x++) { double m = (1 << 16) / pow(2, (x + 1) * (ENV_STEP / 4.0) / 8.0); m = floor(m); // we never reach (1<<16) here due to the (x+1) // result fits within 16 bits at maximum int n = int(m); // 16 bits here n >>= 4; // 12 bits here n = (n >> 1) + (n & 1); // round to nearest // 11 bits here (rounded) n <<= 1; // 12 bits here (as in real chip) tl_tab[x * 2 + 0] = n; tl_tab[x * 2 + 1] = ~tl_tab[x * 2 + 0]; // this _is_ different from OPL2 (verified on real YMF262) for (int i = 1; i < 13; i++) { tl_tab[x * 2 + 0 + i * 2 * TL_RES_LEN] = tl_tab[x * 2 + 0] >> i; tl_tab[x * 2 + 1 + i * 2 * TL_RES_LEN] = ~tl_tab[x * 2 + 0 + i * 2 * TL_RES_LEN]; // this _is_ different from OPL2 (verified on real YMF262) } } const double LOG2 = ::log(2.0); for (int i = 0; i < SIN_LEN; i++) { // non-standard sinus double m = sin(((i * 2) + 1) * M_PI / SIN_LEN); // checked against the real chip // we never reach zero here due to ((i * 2) + 1) double o = (m > 0.0) ? 8 * ::log( 1.0 / m) / LOG2: // convert to 'decibels' 8 * ::log(-1.0 / m) / LOG2; // convert to 'decibels' o = o / (ENV_STEP / 4); int n = int(2 * o); n = (n >> 1) + (n & 1); // round to nearest sin_tab[i] = n * 2 + (m >= 0.0 ? 0 : 1); } for (int i = 0; i < SIN_LEN; ++i) { // these 'pictures' represent _two_ cycles // waveform 1: __ __ // / \____/ \____ // output only first half of the sinus waveform (positive one) sin_tab[1 * SIN_LEN + i] = (i & (1 << (SIN_BITS - 1))) ? TL_TAB_LEN : sin_tab[i]; // waveform 2: __ __ __ __ // / \/ \/ \/ \. // abs(sin) sin_tab[2 * SIN_LEN + i] = sin_tab[i & (SIN_MASK >> 1)]; // waveform 3: _ _ _ _ // / |_/ |_/ |_/ |_ // abs(output only first quarter of the sinus waveform) sin_tab[3 * SIN_LEN + i] = (i & (1 << (SIN_BITS - 2))) ? TL_TAB_LEN : sin_tab[i & (SIN_MASK>>2)]; // waveform 4: /\ ____/\ ____ // \/ \/ // output whole sinus waveform in half the cycle(step=2) // and output 0 on the other half of cycle sin_tab[4 * SIN_LEN + i] = (i & (1 << (SIN_BITS - 1))) ? TL_TAB_LEN : sin_tab[i * 2]; // waveform 5: /\/\____/\/\____ // // output abs(whole sinus) waveform in half the cycle(step=2) // and output 0 on the other half of cycle sin_tab[5 * SIN_LEN + i] = (i & (1 << (SIN_BITS - 1))) ? TL_TAB_LEN : sin_tab[(i * 2) & (SIN_MASK >> 1)]; // waveform 6: ____ ____ // ____ ____ // output maximum in half the cycle and output minimum // on the other half of cycle sin_tab[6 * SIN_LEN + i] = (i & (1 << (SIN_BITS - 1))) ? 1 // negative : 0; // positive // waveform 7:|\____ |\____ // \| \| // output sawtooth waveform int x = (i & (1 << (SIN_BITS - 1))) ? ((SIN_LEN - 1) - i) * 16 + 1 // negative: from 8177 to 1 : i * 16; // positive: from 0 to 8176 x = std::min(x, TL_TAB_LEN); // clip to the allowed range sin_tab[7 * SIN_LEN + i] = x; } } void YMF262Slot::FM_KEYON(byte key_set) { if (!key) { // restart Phase Generator Cnt = FreqIndex(0); // phase -> Attack state = EG_ATTACK; } key |= key_set; } void YMF262Slot::FM_KEYOFF(byte key_clr) { if (key) { key &= ~key_clr; if (!key) { // phase -> Release if (state != EG_OFF) { state = EG_RELEASE; } } } } void YMF262Slot::update_ar_dr() { if ((ar + ksr) < 16 + 60) { // verified on real YMF262 - all 15 x rates take "zero" time eg_sh_ar = eg_rate_shift [ar + ksr]; eg_sel_ar = eg_rate_select[ar + ksr]; } else { eg_sh_ar = 0; eg_sel_ar = 13 * RATE_STEPS; } eg_m_ar = (1 << eg_sh_ar) - 1; eg_sh_dr = eg_rate_shift [dr + ksr]; eg_sel_dr = eg_rate_select[dr + ksr]; eg_m_dr = (1 << eg_sh_dr) - 1; } void YMF262Slot::update_rr() { eg_sh_rr = eg_rate_shift [rr + ksr]; eg_sel_rr = eg_rate_select[rr + ksr]; eg_m_rr = (1 << eg_sh_rr) - 1; } // update phase increment counter of operator (also update the EG rates if necessary) void YMF262Slot::calc_fc(const YMF262Channel& ch) { // (frequency) phase increment counter Incr = ch.fc * mul; int newKsr = ch.kcode >> KSR; if (ksr == newKsr) return; ksr = newKsr; // calculate envelope generator rates update_ar_dr(); update_rr(); } static const unsigned channelPairTab[18] = { 0, 1, 2, 0, 1, 2, unsigned(~0), unsigned(~0), unsigned(~0), 9, 10, 11, 9, 10, 11, unsigned(~0), unsigned(~0), unsigned(~0), }; inline bool YMF262::Impl::isExtended(unsigned ch) const { assert(ch < 18); if (!OPL3_mode) return false; if (channelPairTab[ch] == unsigned(~0)) return false; return channel[channelPairTab[ch]].extended; } static inline unsigned getFirstOfPairNum(unsigned ch) { assert((ch < 18) && (channelPairTab[ch] != unsigned(~0))); return channelPairTab[ch]; } inline YMF262Channel& YMF262::Impl::getFirstOfPair(unsigned ch) { return channel[getFirstOfPairNum(ch) + 0]; } inline YMF262Channel& YMF262::Impl::getSecondOfPair(unsigned ch) { return channel[getFirstOfPairNum(ch) + 3]; } // set multi,am,vib,EG-TYP,KSR,mul void YMF262::Impl::set_mul(unsigned sl, byte v) { unsigned chan_no = sl / 2; YMF262Channel& ch = channel[chan_no]; YMF262Slot& slot = ch.slot[sl & 1]; slot.mul = mul_tab[v & 0x0f]; slot.KSR = (v & 0x10) ? 0 : 2; slot.eg_type = (v & 0x20) != 0; slot.vib = (v & 0x40) != 0; slot.AMmask = (v & 0x80) ? ~0 : 0; if (isExtended(chan_no)) { // 4op mode // update this slot using frequency data for 1st channel of a pair slot.calc_fc(getFirstOfPair(chan_no)); } else { // normal (OPL2 mode or 2op mode) slot.calc_fc(ch); } } // set ksl & tl void YMF262::Impl::set_ksl_tl(unsigned sl, byte v) { unsigned chan_no = sl / 2; YMF262Channel& ch = channel[chan_no]; YMF262Slot& slot = ch.slot[sl & 1]; // This is indeed {0.0, 3.0, 1.5, 6.0} dB/oct, verified on real YMF262. // Note the illogical order of 2nd and 3rd element. static const unsigned ksl_shift[4] = { 31, 1, 2, 0 }; slot.ksl = ksl_shift[v >> 6]; slot.TL = (v & 0x3F) << (ENV_BITS - 1 - 7); // 7 bits TL (bit 6 = always 0) if (isExtended(chan_no)) { // update this slot using frequency data for 1st channel of a pair YMF262Channel& ch0 = getFirstOfPair(chan_no); slot.TLL = slot.TL + (ch0.ksl_base >> slot.ksl); } else { // normal slot.TLL = slot.TL + (ch.ksl_base >> slot.ksl); } } // set attack rate & decay rate void YMF262::Impl::set_ar_dr(unsigned sl, byte v) { YMF262Channel& ch = channel[sl / 2]; YMF262Slot& slot = ch.slot[sl & 1]; slot.ar = (v >> 4) ? 16 + ((v >> 4) << 2) : 0; slot.dr = (v & 0x0F) ? 16 + ((v & 0x0F) << 2) : 0; slot.update_ar_dr(); } // set sustain level & release rate void YMF262::Impl::set_sl_rr(unsigned sl, byte v) { YMF262Channel& ch = channel[sl / 2]; YMF262Slot& slot = ch.slot[sl & 1]; slot.sl = sl_tab[v >> 4]; slot.rr = (v & 0x0F) ? 16 + ((v & 0x0F) << 2) : 0; slot.update_rr(); } byte YMF262::Impl::readReg(unsigned r) { // no need to call updateStream(time) return peekReg(r); } byte YMF262::Impl::peekReg(unsigned r) const { return reg[r]; } void YMF262::Impl::writeReg(unsigned r, byte v, EmuTime::param time) { if (!OPL3_mode && (r != 0x105)) { // in OPL2 mode the only accessible in set #2 is register 0x05 r &= ~0x100; } writeReg512(r, v, time); } void YMF262::Impl::writeReg512(unsigned r, byte v, EmuTime::param time) { updateStream(time); // TODO optimize only for regs that directly influence sound writeRegDirect(r, v, time); } void YMF262::Impl::writeRegDirect(unsigned r, byte v, EmuTime::param time) { reg[r] = v; switch (r) { case 0x104: // 6 channels enable channel[ 0].extended = (v & 0x01) != 0; channel[ 1].extended = (v & 0x02) != 0; channel[ 2].extended = (v & 0x04) != 0; channel[ 9].extended = (v & 0x08) != 0; channel[10].extended = (v & 0x10) != 0; channel[11].extended = (v & 0x20) != 0; return; case 0x105: // OPL3 mode when bit0=1 otherwise it is OPL2 mode OPL3_mode = v & 0x01; // When NEW2 bit is first set, a read from the status register // (once) returns bit 1 set (0x02). This only happens once after // reset, so clearing NEW2 and setting it again doesn't cause // another change in the status register. // This seems strange behaviour to me, but it is what I saw on // a real YMF278. Also see page 10 in the 'OPL4 YMF278B // Application Manual' (though it's not clear on the details). if ((v & 0x02) && !alreadySignaledNEW2 && isYMF278) { status2 = 0x02; alreadySignaledNEW2 = true; } // following behaviour was tested on real YMF262, // switching OPL3/OPL2 modes on the fly: // - does not change the waveform previously selected // (unless when ....) // - does not update CH.A, CH.B, CH.C and CH.D output // selectors (registers c0-c8) (unless when ....) // - does not disable channels 9-17 on OPL3->OPL2 switch // - does not switch 4 operator channels back to 2 // operator channels return; } unsigned ch_offset = (r & 0x100) ? 9 : 0; switch (r & 0xE0) { case 0x00: // 00-1F:control switch (r & 0x1F) { case 0x01: // test register break; case 0x02: // Timer 1 timer1->setValue(v); break; case 0x03: // Timer 2 timer2->setValue(v); break; case 0x04: // IRQ clear / mask and Timer enable if (v & 0x80) { // IRQ flags clear resetStatus(0x60); } else { changeStatusMask((~v) & 0x60); timer1->setStart((v & R04_ST1) != 0, time); timer2->setStart((v & R04_ST2) != 0, time); } break; case 0x08: // x,NTS,x,x, x,x,x,x nts = (v & 0x40) != 0; break; default: break; } break; case 0x20: { // am ON, vib ON, ksr, eg_type, mul int slot = slot_array[r & 0x1F]; if (slot < 0) return; set_mul(slot + ch_offset * 2, v); break; } case 0x40: { int slot = slot_array[r & 0x1F]; if (slot < 0) return; set_ksl_tl(slot + ch_offset * 2, v); break; } case 0x60: { int slot = slot_array[r & 0x1F]; if (slot < 0) return; set_ar_dr(slot + ch_offset * 2, v); break; } case 0x80: { int slot = slot_array[r & 0x1F]; if (slot < 0) return; set_sl_rr(slot + ch_offset * 2, v); break; } case 0xA0: { // note: not r != 0x1BD, only first register block if (r == 0xBD) { // am depth, vibrato depth, r,bd,sd,tom,tc,hh lfo_am_depth = (v & 0x80) != 0; lfo_pm_depth_range = (v & 0x40) ? 8 : 0; rhythm = v & 0x3F; if (rhythm & 0x20) { // BD key on/off if (v & 0x10) { channel[6].slot[MOD].FM_KEYON (2); channel[6].slot[CAR].FM_KEYON (2); } else { channel[6].slot[MOD].FM_KEYOFF(2); channel[6].slot[CAR].FM_KEYOFF(2); } // HH key on/off if (v & 0x01) { channel[7].slot[MOD].FM_KEYON (2); } else { channel[7].slot[MOD].FM_KEYOFF(2); } // SD key on/off if (v & 0x08) { channel[7].slot[CAR].FM_KEYON (2); } else { channel[7].slot[CAR].FM_KEYOFF(2); } // TOM key on/off if (v & 0x04) { channel[8].slot[MOD].FM_KEYON (2); } else { channel[8].slot[MOD].FM_KEYOFF(2); } // TOP-CY key on/off if (v & 0x02) { channel[8].slot[CAR].FM_KEYON (2); } else { channel[8].slot[CAR].FM_KEYOFF(2); } } else { // BD key off channel[6].slot[MOD].FM_KEYOFF(2); channel[6].slot[CAR].FM_KEYOFF(2); // HH key off channel[7].slot[MOD].FM_KEYOFF(2); // SD key off channel[7].slot[CAR].FM_KEYOFF(2); // TOM key off channel[8].slot[MOD].FM_KEYOFF(2); // TOP-CY off channel[8].slot[CAR].FM_KEYOFF(2); } return; } // keyon,block,fnum if ((r & 0x0F) > 8) { return; } unsigned chan_no = (r & 0x0F) + ch_offset; YMF262Channel& ch = channel[chan_no]; int block_fnum; if (!(r & 0x10)) { // a0-a8 block_fnum = (ch.block_fnum & 0x1F00) | v; } else { // b0-b8 block_fnum = ((v & 0x1F) << 8) | (ch.block_fnum & 0xFF); if (isExtended(chan_no)) { if (getFirstOfPairNum(chan_no) == chan_no) { // keyon/off slots of both channels // forming a 4-op channel YMF262Channel& ch0 = getFirstOfPair(chan_no); YMF262Channel& ch3 = getSecondOfPair(chan_no); if (v & 0x20) { ch0.slot[MOD].FM_KEYON(1); ch0.slot[CAR].FM_KEYON(1); ch3.slot[MOD].FM_KEYON(1); ch3.slot[CAR].FM_KEYON(1); } else { ch0.slot[MOD].FM_KEYOFF(1); ch0.slot[CAR].FM_KEYOFF(1); ch3.slot[MOD].FM_KEYOFF(1); ch3.slot[CAR].FM_KEYOFF(1); } } else { // do nothing } } else { // 2 operator function keyon/off if (v & 0x20) { ch.slot[MOD].FM_KEYON (1); ch.slot[CAR].FM_KEYON (1); } else { ch.slot[MOD].FM_KEYOFF(1); ch.slot[CAR].FM_KEYOFF(1); } } } // update if (ch.block_fnum != block_fnum) { ch.block_fnum = block_fnum; ch.ksl_base = ksl_tab[block_fnum >> 6]; ch.fc = fnumToIncrement(block_fnum); // BLK 2,1,0 bits -> bits 3,2,1 of kcode ch.kcode = (ch.block_fnum & 0x1C00) >> 9; // the info below is actually opposite to what is stated // in the Manuals (verifed on real YMF262) // if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum // if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum if (nts) { ch.kcode |= (ch.block_fnum & 0x100) >> 8; // notesel == 1 } else { ch.kcode |= (ch.block_fnum & 0x200) >> 9; // notesel == 0 } if (isExtended(chan_no)) { if (getFirstOfPairNum(chan_no) == chan_no) { // update slots of both channels // forming up 4-op channel // refresh Total Level YMF262Channel& ch0 = getFirstOfPair(chan_no); YMF262Channel& ch3 = getSecondOfPair(chan_no); ch0.slot[MOD].TLL = ch0.slot[MOD].TL + (ch.ksl_base >> ch0.slot[MOD].ksl); ch0.slot[CAR].TLL = ch0.slot[CAR].TL + (ch.ksl_base >> ch0.slot[CAR].ksl); ch3.slot[MOD].TLL = ch3.slot[MOD].TL + (ch.ksl_base >> ch3.slot[MOD].ksl); ch3.slot[CAR].TLL = ch3.slot[CAR].TL + (ch.ksl_base >> ch3.slot[CAR].ksl); // refresh frequency counter ch0.slot[MOD].calc_fc(ch); ch0.slot[CAR].calc_fc(ch); ch3.slot[MOD].calc_fc(ch); ch3.slot[CAR].calc_fc(ch); } else { // nothing } } else { // refresh Total Level in both SLOTs of this channel ch.slot[MOD].TLL = ch.slot[MOD].TL + (ch.ksl_base >> ch.slot[MOD].ksl); ch.slot[CAR].TLL = ch.slot[CAR].TL + (ch.ksl_base >> ch.slot[CAR].ksl); // refresh frequency counter in both SLOTs of this channel ch.slot[MOD].calc_fc(ch); ch.slot[CAR].calc_fc(ch); } } break; } case 0xC0: { // CH.D, CH.C, CH.B, CH.A, FB(3bits), C if ((r & 0xF) > 8) { return; } unsigned chan_no = (r & 0x0F) + ch_offset; YMF262Channel& ch = channel[chan_no]; unsigned base = chan_no * 4; if (OPL3_mode) { // OPL3 mode pan[base + 0] = (v & 0x10) ? unsigned(~0) : 0; // ch.A pan[base + 1] = (v & 0x20) ? unsigned(~0) : 0; // ch.B pan[base + 2] = (v & 0x40) ? unsigned(~0) : 0; // ch.C pan[base + 3] = (v & 0x80) ? unsigned(~0) : 0; // ch.D } else { // OPL2 mode - always enabled pan[base + 0] = unsigned(~0); // ch.A pan[base + 1] = unsigned(~0); // ch.B pan[base + 2] = unsigned(~0); // ch.C pan[base + 3] = unsigned(~0); // ch.D } ch.slot[MOD].setFeedbackShift((v >> 1) & 7); ch.slot[MOD].CON = v & 1; if (isExtended(chan_no)) { unsigned chan_no0 = getFirstOfPairNum(chan_no); unsigned chan_no3 = chan_no0 + 3; YMF262Channel& ch0 = getFirstOfPair(chan_no); YMF262Channel& ch3 = getSecondOfPair(chan_no); switch ((ch0.slot[MOD].CON ? 2:0) | (ch3.slot[MOD].CON ? 1:0)) { case 0: // 1 -> 2 -> 3 -> 4 -> out ch0.slot[MOD].connect = &phase_modulation; ch0.slot[CAR].connect = &phase_modulation2; ch3.slot[MOD].connect = &phase_modulation; ch3.slot[CAR].connect = &chanout[chan_no3]; break; case 1: // 1 -> 2 -\. // 3 -> 4 --+-> out ch0.slot[MOD].connect = &phase_modulation; ch0.slot[CAR].connect = &chanout[chan_no0]; ch3.slot[MOD].connect = &phase_modulation; ch3.slot[CAR].connect = &chanout[chan_no3]; break; case 2: // 1 ----------\. // 2 -> 3 -> 4 -+-> out ch0.slot[MOD].connect = &chanout[chan_no0]; ch0.slot[CAR].connect = &phase_modulation2; ch3.slot[MOD].connect = &phase_modulation; ch3.slot[CAR].connect = &chanout[chan_no3]; break; case 3: // 1 -----\. // 2 -> 3 -+-> out // 4 -----/ ch0.slot[MOD].connect = &chanout[chan_no0]; ch0.slot[CAR].connect = &phase_modulation2; ch3.slot[MOD].connect = &chanout[chan_no3]; ch3.slot[CAR].connect = &chanout[chan_no3]; break; } } else { // 2 operators mode ch.slot[MOD].connect = ch.slot[MOD].CON ? &chanout[chan_no] : &phase_modulation; ch.slot[CAR].connect = &chanout[chan_no]; } break; } case 0xE0: { // waveform select int slot = slot_array[r & 0x1f]; if (slot < 0) return; slot += ch_offset * 2; YMF262Channel& ch = channel[slot / 2]; // store 3-bit value written regardless of current OPL2 or OPL3 // mode... (verified on real YMF262) v &= 7; // ... but select only waveforms 0-3 in OPL2 mode if (!OPL3_mode) { v &= 3; } ch.slot[slot & 1].wavetable = &sin_tab[v * SIN_LEN]; break; } } } void YMF262::Impl::reset(EmuTime::param time) { eg_cnt = 0; noise_rng = 1; // noise shift register nts = false; // note split alreadySignaledNEW2 = false; resetStatus(0x60); // reset with register write writeRegDirect(0x01, 0, time); // test register writeRegDirect(0x02, 0, time); // Timer1 writeRegDirect(0x03, 0, time); // Timer2 writeRegDirect(0x04, 0, time); // IRQ mask clear // FIX IT registers 101, 104 and 105 // FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) for (int c = 0xFF; c >= 0x20; c--) { writeRegDirect(c, 0, time); } // FIX IT (dont change CH.D, CH.C, CH.B and CH.A in C0-C8 registers) for (int c = 0x1FF; c >= 0x120; c--) { writeRegDirect(c, 0, time); } // reset operator parameters for (int c = 0; c < 9 * 2; c++) { YMF262Channel& ch = channel[c]; for (int s = 0; s < 2; s++) { ch.slot[s].state = EG_OFF; ch.slot[s].volume = MAX_ATT_INDEX; } } } YMF262::Impl::Impl(YMF262& self, const std::string& name, const DeviceConfig& config, bool isYMF278_) : ResampledSoundDevice(config.getMotherBoard(), name, "MoonSound FM-part", 18, true) , debuggable(make_unique( config.getMotherBoard(), self, getName())) , timer1(isYMF278_ ? EmuTimer::createOPL4_1(config.getScheduler(), *this) : EmuTimer::createOPL3_1(config.getScheduler(), *this)) , timer2(isYMF278_ ? EmuTimer::createOPL4_2(config.getScheduler(), *this) : EmuTimer::createOPL3_2(config.getScheduler(), *this)) , irq(config.getMotherBoard(), getName() + ".IRQ") , lfo_am_cnt(0), lfo_pm_cnt(0) , isYMF278(isYMF278_) { lfo_am_depth = false; lfo_pm_depth_range = 0; rhythm = 0; OPL3_mode = false; status = status2 = statusMask = 0; // avoid (harmless) UMR in serialize() memset(chanout, 0, sizeof(chanout)); memset(reg, 0, sizeof(reg)); init_tables(); double input = isYMF278 ? 33868800.0 / (19 * 36) : 4 * 3579545.0 / ( 8 * 36); setInputRate(int(input + 0.5)); reset(config.getMotherBoard().getCurrentTime()); registerSound(config); } YMF262::Impl::~Impl() { unregisterSound(); } byte YMF262::Impl::readStatus() { // no need to call updateStream(time) byte result = status | status2; status2 = 0; return result; } byte YMF262::Impl::peekStatus() const { return status | status2; } bool YMF262::Impl::checkMuteHelper() { // TODO this doesn't always mute when possible for (int i = 0; i < 18; i++) { for (int j = 0; j < 2; j++) { YMF262Slot& sl = channel[i].slot[j]; if (!((sl.state == EG_OFF) || ((sl.state == EG_RELEASE) && ((sl.TLL + sl.volume) >= ENV_QUIET)))) { return false; } } } return true; } int YMF262::Impl::getAmplificationFactor() const { return 1 << 2; } void YMF262::Impl::generateChannels(int** bufs, unsigned num) { // TODO implement per-channel mute (instead of all-or-nothing) // TODO output rhythm on separate channels? if (checkMuteHelper()) { // TODO update internal state, even if muted for (int i = 0; i < 18; ++i) { bufs[i] = nullptr; } return; } bool rhythmEnabled = (rhythm & 0x20) != 0; for (unsigned j = 0; j < num; ++j) { // Amplitude modulation: 27 output levels (triangle waveform); // 1 level takes one of: 192, 256 or 448 samples // One entry from LFO_AM_TABLE lasts for 64 samples lfo_am_cnt.addQuantum(); if (lfo_am_cnt == LFOAMIndex(LFO_AM_TAB_ELEMENTS)) { // lfo_am_table is 210 elements long lfo_am_cnt = LFOAMIndex(0); } unsigned tmp = lfo_am_table[lfo_am_cnt.toInt()]; unsigned lfo_am = lfo_am_depth ? tmp : tmp / 4; // clear channel outputs memset(chanout, 0, sizeof(chanout)); // channels 0,3 1,4 2,5 9,12 10,13 11,14 // in either 2op or 4op mode for (int k = 0; k <= 9; k += 9) { for (int i = 0; i < 3; ++i) { YMF262Channel& ch0 = channel[k + i + 0]; YMF262Channel& ch3 = channel[k + i + 3]; // extended 4op ch#0 part 1 or 2op ch#0 ch0.chan_calc(lfo_am); if (ch0.extended) { // extended 4op ch#0 part 2 ch3.chan_calc_ext(lfo_am); } else { // standard 2op ch#3 ch3.chan_calc(lfo_am); } } } // channels 6,7,8 rhythm or 2op mode if (!rhythmEnabled) { channel[6].chan_calc(lfo_am); channel[7].chan_calc(lfo_am); channel[8].chan_calc(lfo_am); } else { // Rhythm part chan_calc_rhythm(lfo_am); } // channels 15,16,17 are fixed 2-operator channels only channel[15].chan_calc(lfo_am); channel[16].chan_calc(lfo_am); channel[17].chan_calc(lfo_am); for (int i = 0; i < 18; ++i) { bufs[i][2 * j + 0] += chanout[i] & pan[4 * i + 0]; bufs[i][2 * j + 1] += chanout[i] & pan[4 * i + 1]; // unused c += chanout[i] & pan[4 * i + 2]; // unused d += chanout[i] & pan[4 * i + 3]; } advance(); } } static enum_string envelopeStateInfo[]= { { "ATTACK", EG_ATTACK }, { "DECAY", EG_DECAY }, { "SUSTAIN", EG_SUSTAIN }, { "RELEASE", EG_RELEASE }, { "OFF", EG_OFF } }; SERIALIZE_ENUM(EnvelopeState, envelopeStateInfo); template void YMF262Slot::serialize(Archive& ar, unsigned /*version*/) { // wavetable unsigned waveform = unsigned((wavetable - sin_tab) / SIN_LEN); ar.serialize("waveform", waveform); if (ar.isLoader()) { wavetable = &sin_tab[waveform * SIN_LEN]; } // done by rewriting registers: // connect, fb_shift, CON // TODO handle more state like this ar.serialize("Cnt", Cnt); ar.serialize("Incr", Incr); ar.serialize("op1_out", op1_out); ar.serialize("TL", TL); ar.serialize("TLL", TLL); ar.serialize("volume", volume); ar.serialize("sl", sl); ar.serialize("state", state); ar.serialize("eg_m_ar", eg_m_ar); ar.serialize("eg_m_dr", eg_m_dr); ar.serialize("eg_m_rr", eg_m_rr); ar.serialize("eg_sh_ar", eg_sh_ar); ar.serialize("eg_sel_ar", eg_sel_ar); ar.serialize("eg_sh_dr", eg_sh_dr); ar.serialize("eg_sel_dr", eg_sel_dr); ar.serialize("eg_sh_rr", eg_sh_rr); ar.serialize("eg_sel_rr", eg_sel_rr); ar.serialize("key", key); ar.serialize("eg_type", eg_type); ar.serialize("AMmask", AMmask); ar.serialize("vib", vib); ar.serialize("ar", this->ar); ar.serialize("dr", dr); ar.serialize("rr", rr); ar.serialize("KSR", KSR); ar.serialize("ksl", ksl); ar.serialize("ksr", ksr); ar.serialize("mul", mul); } template void YMF262Channel::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("slots", slot); ar.serialize("block_fnum", block_fnum); ar.serialize("fc", fc); ar.serialize("ksl_base", ksl_base); ar.serialize("kcode", kcode); ar.serialize("extended", extended); } // version 1: initial version // version 2: added alreadySignaledNEW2 template void YMF262::Impl::serialize(Archive& ar, unsigned version) { ar.serialize("timer1", *timer1); ar.serialize("timer2", *timer2); ar.serialize("irq", irq); ar.serialize("chanout", chanout); ar.serialize_blob("registers", reg, sizeof(reg)); ar.serialize("channels", channel); ar.serialize("eg_cnt", eg_cnt); ar.serialize("noise_rng", noise_rng); ar.serialize("lfo_am_cnt", lfo_am_cnt); ar.serialize("lfo_pm_cnt", lfo_pm_cnt); ar.serialize("lfo_am_depth", lfo_am_depth); ar.serialize("lfo_pm_depth_range", lfo_pm_depth_range); ar.serialize("rhythm", rhythm); ar.serialize("nts", nts); ar.serialize("OPL3_mode", OPL3_mode); ar.serialize("status", status); ar.serialize("status2", status2); ar.serialize("statusMask", statusMask); if (ar.versionAtLeast(version, 2)) { ar.serialize("alreadySignaledNEW2", alreadySignaledNEW2); } // TODO restore more state by rewriting register values // this handles pan EmuTime::param time = timer1->getCurrentTime(); for (int i = 0xC0; i <= 0xC8; ++i) { writeRegDirect(i + 0x000, reg[i + 0x000], time); writeRegDirect(i + 0x100, reg[i + 0x100], time); } } // SimpleDebuggable YMF262Debuggable::YMF262Debuggable(MSXMotherBoard& motherBoard, YMF262& ymf262_, const std::string& name) : SimpleDebuggable(motherBoard, name + " regs", "MoonSound FM-part registers", 0x200) , ymf262(ymf262_) { } byte YMF262Debuggable::read(unsigned address) { return ymf262.peekReg(address); } void YMF262Debuggable::write(unsigned address, byte value, EmuTime::param time) { ymf262.writeReg512(address, value, time); } // class YMF262 YMF262::YMF262(const std::string& name, const DeviceConfig& config, bool isYMF278) : pimpl(make_unique(*this, name, config, isYMF278)) { } YMF262::~YMF262() { } void YMF262::reset(EmuTime::param time) { pimpl->reset(time); } void YMF262::writeReg(unsigned r, byte v, EmuTime::param time) { pimpl->writeReg(r, v, time); } void YMF262::writeReg512(unsigned r, byte v, EmuTime::param time) { pimpl->writeReg512(r, v, time); } byte YMF262::readReg(unsigned reg) { return pimpl->readReg(reg); } byte YMF262::peekReg(unsigned reg) const { return pimpl->peekReg(reg); } byte YMF262::readStatus() { return pimpl->readStatus(); } byte YMF262::peekStatus() const { return pimpl->peekStatus(); } template void YMF262::serialize(Archive& ar, unsigned version) { pimpl->serialize(ar, version); } INSTANTIATE_SERIALIZE_METHODS(YMF262); } // namespace openmsx openmsx-0.10.0/src/sound/WavWriter.cc0000644000175000017500000000756512262345041020241 0ustar manuelmanuel00000000000000#include "WavWriter.hh" #include "File.hh" #include "MSXException.hh" #include "Math.hh" #include "vla.hh" #include "endian.hh" #include "memory.hh" #include #include #include namespace openmsx { WavWriter::WavWriter(const Filename& filename, unsigned channels, unsigned bits, unsigned frequency) : file(make_unique(filename, "wb")) , bytes(0) { // write wav header struct WavHeader { char chunkID[4]; // + 0 'RIFF' Endian::L32 chunkSize; // + 4 total size char format[4]; // + 8 'WAVE' char subChunk1ID[4]; // +12 'fmt ' Endian::L32 subChunk1Size; // +16 = 16 (fixed) Endian::L16 audioFormat; // +20 = 1 (fixed) Endian::L16 numChannels; // +22 Endian::L32 sampleRate; // +24 Endian::L32 byteRate; // +28 Endian::L16 blockAlign; // +32 Endian::L16 bitsPerSample; // +34 char subChunk2ID[4]; // +36 'data' Endian::L32 subChunk2Size; // +40 } header; memcpy(header.chunkID, "RIFF", sizeof(header.chunkID)); header.chunkSize = 0; // actual value filled in later memcpy(header.format, "WAVE", sizeof(header.format)); memcpy(header.subChunk1ID, "fmt ", sizeof(header.subChunk1ID)); header.subChunk1Size = 16; header.audioFormat = 1; header.numChannels = channels; header.sampleRate = frequency; header.byteRate = (channels * frequency * bits) / 8; header.blockAlign = (channels * bits) / 8; header.bitsPerSample = bits; memcpy(header.subChunk2ID, "data", sizeof(header.subChunk2ID)); header.subChunk2Size = 0; // actaul value filled in later file->write(&header, sizeof(header)); } WavWriter::~WavWriter() { try { // data chunk must have an even number of bytes if (bytes & 1) { unsigned char pad = 0; file->write(&pad, 1); } flush(); // write header } catch (MSXException&) { // ignore, can't throw from destructor } } bool WavWriter::isEmpty() const { return bytes == 0; } void WavWriter::flush() { // TODO For now (before C++11) this needs separate definition and // initialization. See comments in Endian::EndianT for details. Endian::L32 totalSize, wavSize; totalSize = (bytes + 44 - 8 + 1) & ~1; // round up to even number wavSize = bytes; file->seek(4); file->write(&totalSize, 4); file->seek(40); file->write(&wavSize, 4); file->seek(file->getSize()); // SEEK_END file->flush(); } void Wav8Writer::write(const unsigned char* buffer, unsigned samples) { file->write(buffer, samples); bytes += samples; } void Wav16Writer::write(const short* buffer, unsigned samples) { unsigned size = sizeof(short) * samples; if (OPENMSX_BIGENDIAN) { // Variable length arrays (VLA) are part of c99 but not of c++ // (not even c++11). Some compilers (like gcc) do support VLA // in c++ mode, others (like VC++) don't. Still other compilers // (like clang) only support VLA for POD types. // To side-step this issue we simply use a std::vector, this // code is anyway not performance critical. //VLA(Endian::L16, buf, samples); // doesn't work in clang //std::vector buf(buffer, buffer + samples); // this needs c++11 std::vector buf(samples); for (unsigned i = 0; i < samples; ++i) { buf[i] = buffer[i]; } file->write(buf.data(), size); } else { file->write(buffer, size); } bytes += size; } void Wav16Writer::write(const int* buffer, unsigned samples, int amp) { //VLA(Endian::L16, buf, samples); // doesn't work in clang std::vector buf(samples); for (unsigned i = 0; i < samples; ++i) { buf[i] = Math::clipIntToShort(buffer[i] * amp); } unsigned size = sizeof(short) * samples; file->write(buf.data(), size); bytes += size; } void Wav16Writer::writeSilence(unsigned samples) { VLA(short, buf, samples); unsigned size = sizeof(short) * samples; memset(buf, 0, size); file->write(buf, size); bytes += size; } } // namespace openmsx openmsx-0.10.0/src/sound/VLM5030.hh0000644000175000017500000000131512262345041017252 0ustar manuelmanuel00000000000000#ifndef VLM5030_HH #define VLM5030_HH #include "EmuTime.hh" #include "openmsx.hh" #include #include namespace openmsx { class DeviceConfig; class VLM5030 { public: VLM5030(const std::string& name, const std::string& desc, const std::string& romFilename, const DeviceConfig& config); ~VLM5030(); void reset(); /** latch control data */ void writeData(byte data); /** set RST / VCU / ST pins */ void writeControl(byte data, EmuTime::param time); /** get BSY pin level */ bool getBSY(EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: class Impl; const std::unique_ptr pimpl; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/AudioInputDevice.cc0000644000175000017500000000025712262345041021477 0ustar manuelmanuel00000000000000#include "AudioInputDevice.hh" using std::string; namespace openmsx { string_ref AudioInputDevice::getClass() const { return "Audio Input Port"; } } // namespace openmsx openmsx-0.10.0/src/sound/AudioInputConnector.hh0000644000175000017500000000117112262345041022240 0ustar manuelmanuel00000000000000#ifndef AUDIOINPUTCONNECTOR_HH #define AUDIOINPUTCONNECTOR_HH #include "Connector.hh" namespace openmsx { class AudioInputDevice; class AudioInputConnector : public Connector { public: AudioInputConnector(PluggingController& pluggingController, string_ref name); virtual ~AudioInputConnector(); AudioInputDevice& getPluggedAudioDev() const; // Connector virtual const std::string getDescription() const; virtual string_ref getClass() const; short readSample(EmuTime::param time) const; template void serialize(Archive& ar, unsigned version); }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXYamahaSFG.hh0000644000175000017500000000201612262345041020433 0ustar manuelmanuel00000000000000#ifndef MSXYAMAHASFG_HH #define MSXYAMAHASFG_HH #include "MSXDevice.hh" #include namespace openmsx { class Rom; class YM2151; class YM2148; class MSXYamahaSFG : public MSXDevice { public: explicit MSXYamahaSFG(const DeviceConfig& config); virtual ~MSXYamahaSFG(); virtual void reset(EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual const byte* getReadCacheLine(word start) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual byte* getWriteCacheLine(word start) const; virtual byte readIRQVector(); template void serialize(Archive& ar, unsigned version); private: void writeRegisterPort(byte value, EmuTime::param time); void writeDataPort(byte value, EmuTime::param time); const std::unique_ptr rom; const std::unique_ptr ym2151; const std::unique_ptr ym2148; int registerLatch; byte irqVector; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXMusic.cc0000644000175000017500000000340312262345041017742 0ustar manuelmanuel00000000000000#include "MSXMusic.hh" #include "YM2413.hh" #include "Rom.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { MSXMusic::MSXMusic(const DeviceConfig& config) : MSXDevice(config) , rom(make_unique(getName() + " ROM", "rom", config)) , ym2413(make_unique(getName(), config)) { reset(getCurrentTime()); } MSXMusic::~MSXMusic() { } void MSXMusic::reset(EmuTime::param time) { ym2413->reset(time); registerLatch = 0; // TODO check } void MSXMusic::writeIO(word port, byte value, EmuTime::param time) { switch (port & 0x01) { case 0: writeRegisterPort(value, time); break; case 1: writeDataPort(value, time); break; } } void MSXMusic::writeRegisterPort(byte value, EmuTime::param /*time*/) { registerLatch = value & 0x3F; } void MSXMusic::writeDataPort(byte value, EmuTime::param time) { //PRT_DEBUG("YM2413: reg "<<(int)registerLatch<<" val "<<(int)value); ym2413->writeReg(registerLatch, value, time); } byte MSXMusic::readMem(word address, EmuTime::param /*time*/) { return *MSXMusic::getReadCacheLine(address); } const byte* MSXMusic::getReadCacheLine(word start) const { return &(*rom)[start & (rom->getSize() - 1)]; } // version 1: initial version // version 2: refactored YM2413 class structure template void MSXMusic::serialize(Archive& ar, unsigned version) { ar.template serializeBase(*this); if (ar.versionAtLeast(version, 2)) { ar.serialize("ym2413", *ym2413); } else { // In older versions, the 'ym2413' level was missing, delegate // directly to YM2413 without emitting the 'ym2413' tag. ym2413->serialize(ar, version); } ar.serialize("registerLatch", registerLatch); } INSTANTIATE_SERIALIZE_METHODS(MSXMusic); REGISTER_MSXDEVICE(MSXMusic, "MSX-Music"); } // namespace openmsx openmsx-0.10.0/src/sound/SCC.cc0000644000175000017500000004351412262345041016711 0ustar manuelmanuel00000000000000//----------------------------------------------------------------------------- // // On Mon, 24 Feb 2003, Jon De Schrijder wrote: // // I've done some measurements with the scope on the output of the SCC. // I didn't do timing tests, only amplitude checks: // // I know now for sure, the amplitude calculation works as follows: // // AmpOut=640+AmpA+AmpB+AmpC+AmpD+AmpE // // range AmpOut (11 bits positive number=SCC digital output): [+40...+1235] // // AmpA="((SampleValue*VolA) AND #7FF0) div 16" // AmpB="((SampleValue*VolB) AND #7FF0) div 16" // AmpC="((SampleValue*VolC) AND #7FF0) div 16" // AmpD="((SampleValue*VolD) AND #7FF0) div 16" // AmpE="((SampleValue*VolE) AND #7FF0) div 16" // // Setting the enablebit to zero, corresponds with VolX=0. // // SampleValue range [-128...+127] // VolX range [0..15] // // Notes: // * SampleValue*VolX is calculated (signed multiplication) and the lower 4 // bits are dropped (both in case the value is positive or negative), before // the addition of the 5 values is done. This was tested by setting // SampleValue=+1 and VolX=15 of different channels. The resulting AmpOut=640, // indicating that the 4 lower bits were dropped *before* the addition. // //----------------------------------------------------------------------------- // // On Mon, 14 Apr 2003, Manuel Pazos wrote // // I have some info about SCC/SCC+ that I hope you find useful. It is about // "Mode Setting Register", also called "Deformation Register" Here it goes: // // bit0: 4 bits frequency (%XXXX00000000). Equivalent to // (normal frequency >> 8) bits0-7 are ignored // bit1: 8 bits frequency (%0000XXXXXXXX) bits8-11 are ignored // bit2: // bit3: // bit4: // bit5: wave data is played from begining when frequency is changed // bit6: rotate all waves data. You can't write to them. Rotation speed // =3.58Mhz / (channel i frequency + 1) // bit7: rotate channel 4 wave data. You can't write to that channel // data.ONLY works in MegaROM SCC (not in SCC+) // // If bit7 and bit6 are set, only channel 1-3 wave data rotates . You can't // write to ANY wave data. And there is a weird behaviour in this setting. It // seems SCC sound is corrupted in anyway with MSX databus or so. Try to // activate them (with proper waves, freqs, and vol.) and execute DIR command // on DOS. You will hear "noise" This seems to be fixed in SCC+ // // Reading Mode Setting Register, is equivalent to write #FF to it. // // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // // Additions: // - Setting both bit0 and bit1 is equivalent to setting only bit1 // - A rotation goes like this: // wavedata[0:31] = wavedata[1:31].wavedata[0] // - Channel 4-5 rotation speed is set by channel 5 freq (channel 4 freq // is ignored for rotation) // // Also see this MRC thread: // http://www.msx.org/forumtopicl7875.html // //----------------------------------------------------------------------------- // // On Sat, 09 Sep 2005, NYYRIKKI wrote (MRC post) // // ... // // One important thing to know is that change of volume is not implemented // immediately in SCC. Normally it is changed when next byte from sample memory // is played, but writing value to frequency causes current byte to be started // again. As in this example we write values very quickly to frequency registers // the internal sample counter does not actually move at all. // // Third method is a variation of first method. As we don't know where SCC is // playing, let's update the whole sample memory with one and same new value. // To make sample rate not variable in low sample rates we first stop SCC from // reading sample memory. This can be done by writing value less than 9 to // frequency. Now we can update sample RAM so, that output does not change. // After sample RAM has been updated, we start SCC internal counter so that // value (where ever the counter was) is sent to output. This routine can be // found below as example 3. // // ... // // // // Something completely different: the SCC+ is actually called SCC-I. //----------------------------------------------------------------------------- #include "SCC.hh" #include "SimpleDebuggable.hh" #include "DeviceConfig.hh" #include "serialize.hh" #include "likely.hh" #include "unreachable.hh" #include "memory.hh" using std::string; namespace openmsx { class SCCDebuggable : public SimpleDebuggable { public: SCCDebuggable(MSXMotherBoard& motherBoard, SCC& scc); virtual byte read(unsigned address, EmuTime::param time); virtual void write(unsigned address, byte value, EmuTime::param time); private: SCC& scc; }; static string calcDescription(SCC::ChipMode mode) { return (mode == SCC::SCC_Real) ? "Konami SCC" : "Konami SCC+"; } SCC::SCC(const string& name, const DeviceConfig& config, EmuTime::param time, ChipMode mode) : ResampledSoundDevice( config.getMotherBoard(), name, calcDescription(mode), 5) , debuggable(make_unique( config.getMotherBoard(), *this)) , deformTimer(time) , currentChipMode(mode) { // Make valgrind happy for (int i = 0; i < 5; ++i) { orgPeriod[i] = 0; } double input = 3579545.0 / 32; setInputRate(int(input + 0.5)); powerUp(time); registerSound(config); } SCC::~SCC() { unregisterSound(); } void SCC::powerUp(EmuTime::param time) { // Power on values, tested by enen (log from IRC #openmsx): // // wouter_: i did an scc poweron values test, deform=0, // amplitude=full, channelenable=0, period=under 8 // ... // did you test the value of the waveforms as well? // ... // filled with $FF, some bits cleared but that seems random // Initialize ch_enable, deform (initialize this before period) reset(time); // Initialize waveform (initialize before volumes) for (unsigned i = 0; i < 5; ++i) { for (unsigned j = 0; j < 32; ++j) { wave[i][j] = ~0; } } // Initialize volume (initialize this before period) for (int i = 0; i < 5; ++i) { setFreqVol(i + 10, 15, time); } // Actual initial value is difficult to measure, assume zero // (initialize before period) for (unsigned i = 0; i < 5; ++i) { pos[i] = 0; } // Initialize period (sets members orgPeriod, period, incr, count, out) for (int i = 0; i < 2 * 5; ++i) { setFreqVol(i, 0, time); } } void SCC::reset(EmuTime::param /*time*/) { if (currentChipMode != SCC_Real) { setChipMode(SCC_Compatible); } setDeformRegHelper(0); ch_enable = 0; } void SCC::setChipMode(ChipMode newMode) { if (currentChipMode == SCC_Real) { assert(newMode == SCC_Real); } else { assert(newMode != SCC_Real); } currentChipMode = newMode; } byte SCC::readMem(byte addr, EmuTime::param time) { // Deform-register locations: // SCC_Real: 0xE0..0xFF // SCC_Compatible: 0xC0..0xDF // SCC_plusmode: 0xC0..0xDF if (((currentChipMode == SCC_Real) && (addr >= 0xE0)) || ((currentChipMode != SCC_Real) && (0xC0 <= addr) && (addr < 0xE0))) { setDeformReg(0xFF, time); } return peekMem(addr, time); } byte SCC::peekMem(byte address, EmuTime::param time) const { byte result; switch (currentChipMode) { case SCC_Real: if (address < 0x80) { // 0x00..0x7F : read wave form 1..4 result = readWave(address >> 5, address, time); } else { // 0x80..0x9F : freq volume block, write only // 0xA0..0xDF : no function // 0xE0..0xFF : deformation register result = 0xFF; } break; case SCC_Compatible: if (address < 0x80) { // 0x00..0x7F : read wave form 1..4 result = readWave(address >> 5, address, time); } else if (address < 0xA0) { // 0x80..0x9F : freq volume block result = 0xFF; } else if (address < 0xC0) { // 0xA0..0xBF : read wave form 5 result = readWave(4, address, time); } else { // 0xC0..0xDF : deformation register // 0xE0..0xFF : no function result = 0xFF; } break; case SCC_plusmode: if (address < 0xA0) { // 0x00..0x9F : read wave form 1..5 result = readWave(address >> 5, address, time); } else { // 0xA0..0xBF : freq volume block // 0xC0..0xDF : deformation register // 0xE0..0xFF : no function result = 0xFF; } break; default: UNREACHABLE; return 0; } return result; } byte SCC::readWave(unsigned channel, unsigned address, EmuTime::param time) const { if (!rotate[channel]) { return wave[channel][address & 0x1F]; } else { unsigned ticks = deformTimer.getTicksTill(time); unsigned periodCh = ((channel == 3) && (currentChipMode != SCC_plusmode) && ((deformValue & 0xC0) == 0x40)) ? 4 : channel; unsigned shift = ticks / (period[periodCh] + 1); return wave[channel][(address + shift) & 0x1F]; } } byte SCC::getFreqVol(unsigned address) const { address &= 0x0F; if (address < 0x0A) { // get frequency unsigned channel = address / 2; if (address & 1) { return orgPeriod[channel] >> 8; } else { return orgPeriod[channel] & 0xFF; } } else if (address < 0x0F) { // get volume return volume[address - 0xA]; } else { // get enable-bits return ch_enable; } } void SCC::writeMem(byte address, byte value, EmuTime::param time) { updateStream(time); switch (currentChipMode) { case SCC_Real: if (address < 0x80) { // 0x00..0x7F : write wave form 1..4 writeWave(address >> 5, address, value); } else if (address < 0xA0) { // 0x80..0x9F : freq volume block setFreqVol(address, value, time); } else if (address < 0xE0) { // 0xA0..0xDF : no function } else { // 0xE0..0xFF : deformation register setDeformReg(value, time); } break; case SCC_Compatible: if (address < 0x80) { // 0x00..0x7F : write wave form 1..4 writeWave(address >> 5, address, value); } else if (address < 0xA0) { // 0x80..0x9F : freq volume block setFreqVol(address, value, time); } else if (address < 0xC0) { // 0xA0..0xBF : ignore write wave form 5 } else if (address < 0xE0) { // 0xC0..0xDF : deformation register setDeformReg(value, time); } else { // 0xE0..0xFF : no function } break; case SCC_plusmode: if (address < 0xA0) { // 0x00..0x9F : write wave form 1..5 writeWave(address >> 5, address, value); } else if (address < 0xC0) { // 0xA0..0xBF : freq volume block setFreqVol(address, value, time); } else if (address < 0xE0) { // 0xC0..0xDF : deformation register setDeformReg(value, time); } else { // 0xE0..0xFF : no function } break; default: UNREACHABLE; } } int SCC::getAmplificationFactor() const { return 256; } inline int SCC::adjust(signed char wav, byte vol) { return (int(wav) * vol) >> 4; } void SCC::writeWave(unsigned channel, unsigned address, byte value) { // write to channel 5 only possible in SCC+ mode assert(channel < 5); assert((channel != 4) || (currentChipMode == SCC_plusmode)); if (!readOnly[channel]) { unsigned pos = address & 0x1F; wave[channel][pos] = value; volAdjustedWave[channel][pos] = adjust(value, volume[channel]); if ((currentChipMode != SCC_plusmode) && (channel == 3)) { // copy waveform 4 -> waveform 5 wave[4][pos] = wave[3][pos]; volAdjustedWave[4][pos] = adjust(value, volume[4]); } } } void SCC::setFreqVol(unsigned address, byte value, EmuTime::param time) { address &= 0x0F; // region is visible twice if (address < 0x0A) { // change frequency unsigned channel = address / 2; unsigned per = (address & 1) ? ((value & 0xF) << 8) | (orgPeriod[channel] & 0xFF) : (orgPeriod[channel] & 0xF00) | (value & 0xFF); orgPeriod[channel] = per; if (deformValue & 2) { // 8 bit frequency per &= 0xFF; } else if (deformValue & 1) { // 4 bit frequency per >>= 8; } period[channel] = per; incr[channel] = (per <= 8) ? 0 : 32; count[channel] = 0; // reset to begin of byte if (deformValue & 0x20) { pos[channel] = 0; // reset to begin of waveform // also 'rotation' mode (confirmed by test based on // Artag's SCC sample player) deformTimer.advance(time); } // after a freq change, update the output out[channel] = volAdjustedWave[channel][pos[channel]]; } else if (address < 0x0F) { // change volume unsigned channel = address - 0x0A; volume[channel] = value & 0xF; for (unsigned i = 0; i < 32; ++i) { volAdjustedWave[channel][i] = adjust(wave[channel][i], volume[channel]); } } else { // change enable-bits ch_enable = value; } } void SCC::setDeformReg(byte value, EmuTime::param time) { if (value == deformValue) { return; } deformTimer.advance(time); setDeformRegHelper(value); } void SCC::setDeformRegHelper(byte value) { deformValue = value; if (currentChipMode != SCC_Real) { value &= ~0x80; } switch (value & 0xC0) { case 0x00: for (unsigned i = 0; i < 5; ++i) { rotate[i] = false; readOnly[i] = false; } break; case 0x40: for (unsigned i = 0; i < 5; ++i) { rotate[i] = true; readOnly[i] = true; } break; case 0x80: for (unsigned i = 0; i < 3; ++i) { rotate[i] = false; readOnly[i] = false; } for (unsigned i = 3; i < 5; ++i) { rotate[i] = true; readOnly[i] = true; } break; case 0xC0: for (unsigned i = 0; i < 3; ++i) { rotate[i] = true; readOnly[i] = true; } for (unsigned i = 3; i < 5; ++i) { rotate[i] = false; readOnly[i] = true; } break; default: UNREACHABLE; } } void SCC::generateChannels(int** bufs, unsigned num) { unsigned enable = ch_enable; for (unsigned i = 0; i < 5; ++i, enable >>= 1) { if ((enable & 1) && (volume[i] || out[i])) { #ifdef __arm__ unsigned dummy; int* buf = bufs[i]; asm volatile ( "0:\n\t" "ldr %[T],[%[B]]\n\t" "add %[T],%[T],%[O]\n\t" "add %[C],%[C],%[I]\n\t" "str %[T],[%[B]],#4\n\t" "subs %[T],%[C],%[PE]\n\t" "bpl 2f\n" "1:\n\t" "cmp %[B],%[E]\n\t" "bne 0b\n\t" "b 3f\n" "2:\n\t" "adds %[PO],%[PO],#1\n\t" "subs %[T],%[T],%[PE]\n\t" "bpl 2b\n\t" "and %[PO],%[PO],#31\n\t" "add %[C],%[T],%[PE]\n\t" "ldr %[O],[%[V],%[PO],LSL #2]\n\t" "b 1b\n" "3:\n\t" : [T] "=&r" (dummy) , [B] "=r" (buf) , [O] "=r" (out[i]) , [C] "=r" (count[i]) , [PO] "=r" (pos[i]) : "[B]" (buf) , "[O]" (out[i]) , "[C]" (count[i]) , [I] "r" (incr[i]) , [PE] "r" (period[i] + 1) , [E] "r" (&buf[num]) , "[PO]" (pos[i]) , [V] "r" (volAdjustedWave[i]) : "memory", "cc" ); #else int out2 = out[i]; unsigned count2 = count[i]; unsigned pos2 = pos[i]; unsigned incr2 = incr[i]; unsigned period2 = period[i] + 1; for (unsigned j = 0; j < num; ++j) { bufs[i][j] += out2; count2 += incr2; // Note: only for very small periods // this will take more than 1 iteration while (unlikely(count2 >= period2)) { count2 -= period2; pos2 = (pos2 + 1) % 32; out2 = volAdjustedWave[i][pos2]; } } out[i] = out2; count[i] = count2; pos[i] = pos2; #endif } else { bufs[i] = nullptr; // channel muted // Update phase counter. unsigned newCount = count[i] + num * incr[i]; count[i] = newCount % (period[i] + 1); pos[i] = (pos[i] + newCount / (period[i] + 1)) % 32; // Channel stays off until next waveform index. out[i] = 0; } } } // SimpleDebuggable SCCDebuggable::SCCDebuggable(MSXMotherBoard& motherBoard, SCC& scc_) : SimpleDebuggable(motherBoard, scc_.getName() + " SCC", "SCC registers in SCC+ format", 0x100) , scc(scc_) { } byte SCCDebuggable::read(unsigned address, EmuTime::param time) { if (address < 0xA0) { // read wave form 1..5 return scc.readWave(address >> 5, address, time); } else if (address < 0xC0) { // freq volume block return scc.getFreqVol(address); } else if (address < 0xE0) { // peek deformation register return scc.deformValue; } else { return 0xFF; } } void SCCDebuggable::write(unsigned address, byte value, EmuTime::param time) { if (address < 0xA0) { // read wave form 1..5 scc.writeWave(address >> 5, address, value); } else if (address < 0xC0) { // freq volume block scc.setFreqVol(address, value, time); } else if (address < 0xE0) { // deformation register scc.setDeformReg(value, time); } else { // ignore } } static enum_string chipModeInfo[] = { { "Real", SCC::SCC_Real }, { "Compatible", SCC::SCC_Compatible }, { "Plus", SCC::SCC_plusmode }, }; SERIALIZE_ENUM(SCC::ChipMode, chipModeInfo); template void SCC::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("mode", currentChipMode); ar.serialize("period", orgPeriod); ar.serialize("volume", volume); ar.serialize("ch_enable", ch_enable); ar.serialize("deformTimer", deformTimer); ar.serialize("deform", deformValue); // multi-dimensional arrays are not directly support by the // serialization framework, maybe in the future. So for now // manually loop over the channels. char tag[6] = { 'w', 'a', 'v', 'e', 'X', 0 }; for (int channel = 0; channel < 5; ++channel) { tag[4] = char('1' + channel); ar.serialize(tag, wave[channel]); // signed char } if (ar.isLoader()) { // recalculate volAdjustedWave for (int channel = 0; channel < 5; ++channel) { for (int pos = 0; pos < 32; ++pos) { volAdjustedWave[channel][pos] = adjust(wave[channel][pos], volume[channel]); } } // recalculate rotate[5] and readOnly[5] setDeformRegHelper(deformValue); // recalculate incr[5] and period[5] // this also (possibly) changes count[5], pos[5] and out[5] // as an unwanted side-effect, so (de)serialize those later // Don't use current time, but instead use deformTimer, to // avoid changing the value of deformTimer. EmuTime::param time = deformTimer.getTime(); for (int channel = 0; channel < 5; ++channel) { unsigned per = orgPeriod[channel]; setFreqVol(2 * channel + 0, (per & 0x0FF) >> 0, time); setFreqVol(2 * channel + 1, (per & 0xF00) >> 8, time); } } // call to setFreqVol() modifies these variables, see above ar.serialize("count", count); ar.serialize("pos", pos); ar.serialize("out", out); } INSTANTIATE_SERIALIZE_METHODS(SCC); } // namespace openmsx openmsx-0.10.0/src/sound/AY8910.cc0000644000175000017500000007045212262345041017135 0ustar manuelmanuel00000000000000/* * Emulation of the AY-3-8910 * * Original code taken from xmame-0.37b16.1 * Based on various code snippets by Ville Hallik, Michael Cuddy, * Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria. * Integrated into openMSX by ???. * Refactored in C++ style by Maarten ter Huurne. */ #include "AY8910.hh" #include "AY8910Periphery.hh" #include "DeviceConfig.hh" #include "SimpleDebuggable.hh" #include "DeviceConfig.hh" #include "TclCallback.hh" #include "GlobalSettings.hh" #include "MSXException.hh" #include "StringOp.hh" #include "FloatSetting.hh" #include "serialize.hh" #include "likely.hh" #include "memory.hh" #include #include #include using std::string; namespace openmsx { class AY8910Debuggable : public SimpleDebuggable { public: AY8910Debuggable(MSXMotherBoard& motherBoard, AY8910& ay8910); virtual byte read(unsigned address, EmuTime::param time); virtual void write(unsigned address, byte value, EmuTime::param time); private: AY8910& ay8910; }; // The step clock for the tone and noise generators is the chip clock // divided by 8; for the envelope generator of the AY-3-8910, it is half // that much (clock/16). static const double NATIVE_FREQ_DOUBLE = (3579545.0 / 2) / 8; static const int NATIVE_FREQ_INT = int(NATIVE_FREQ_DOUBLE + 0.5); static const int PORT_A_DIRECTION = 0x40; static const int PORT_B_DIRECTION = 0x80; enum Register { AY_AFINE = 0, AY_ACOARSE = 1, AY_BFINE = 2, AY_BCOARSE = 3, AY_CFINE = 4, AY_CCOARSE = 5, AY_NOISEPER = 6, AY_ENABLE = 7, AY_AVOL = 8, AY_BVOL = 9, AY_CVOL = 10, AY_EFINE = 11, AY_ECOARSE = 12, AY_ESHAPE = 13, AY_PORTA = 14, AY_PORTB = 15 }; // Perlin noise static float n[256 + 3]; static void initDetune() { for (int i = 0; i < 256; ++i) { n[i] = float(rand()) / (RAND_MAX / 2) - 1.0f; } n[256] = n[0]; n[257] = n[1]; n[258] = n[2]; } static float noiseValue(float x) { // cubic hermite spline interpolation assert(0.0f <= x); int xi = int(x); float xf = x - xi; xi &= 255; float n0 = n[xi + 0]; float n1 = n[xi + 1]; float n2 = n[xi + 2]; float n3 = n[xi + 3]; float a = n3 - n2 + n1 - n0; float b = n0 - n1 - a; float c = n2 - n0; float d = n1; return ((a * xf + b) * xf + c) * xf + d; } // Generator: AY8910::Generator::Generator() { reset(0); } inline void AY8910::Generator::reset(unsigned output) { count = 0; this->output = output; } inline void AY8910::Generator::setPeriod(int value) { // Careful studies of the chip output prove that it instead counts up from // 0 until the counter becomes greater or equal to the period. This is an // important difference when the program is rapidly changing the period to // modulate the sound. // Also, note that period = 0 is the same as period = 1. This is mentioned // in the YM2203 data sheets. However, this does NOT apply to the Envelope // period. In that case, period = 0 is half as period = 1. period = std::max(1, value); count = std::min(count, period - 1); } inline unsigned AY8910::Generator::getOutput() const { return output; } inline unsigned AY8910::Generator::getNextEventTime() const { assert(count < period); return period - count; } inline void AY8910::Generator::advanceFast(unsigned duration) { count += duration; assert(count < period); } // ToneGenerator: AY8910::ToneGenerator::ToneGenerator() : parent(nullptr), vibratoCount(0), detuneCount(0) { } inline void AY8910::ToneGenerator::setParent(AY8910& parent) { this->parent = &parent; } int AY8910::ToneGenerator::getDetune() { int result = 0; float vibPerc = parent->vibratoPercent->getDouble(); if (vibPerc != 0.0f) { int vibratoPeriod = int( NATIVE_FREQ_DOUBLE / parent->vibratoFrequency->getDouble()); vibratoCount += period; vibratoCount %= vibratoPeriod; result += int( sinf((float(2 * M_PI) * vibratoCount) / vibratoPeriod) * vibPerc * 0.01f * period); } float detunePerc = parent->detunePercent->getDouble(); if (detunePerc != 0.0f) { float detunePeriod = NATIVE_FREQ_DOUBLE / parent->detuneFrequency->getDouble(); detuneCount += period; float noiseIdx = detuneCount / detunePeriod; float noise = noiseValue( noiseIdx) + noiseValue(2.0f * noiseIdx) / 2.0f; result += int(noise * detunePerc * 0.01f * period); } return std::min(result, period - 1); } inline void AY8910::ToneGenerator::advance(int duration) { assert(count < period); count += duration; if (count >= period) { // Calculate number of output transitions. int cycles = count / period; count -= period * cycles; // equivalent to count %= period; output ^= cycles & 1; } } inline void AY8910::ToneGenerator::doNextEvent(bool doDetune) { if (unlikely(doDetune)) { count = getDetune(); } else { count = 0; } output ^= 1; } // NoiseGenerator: AY8910::NoiseGenerator::NoiseGenerator() { reset(); } inline void AY8910::NoiseGenerator::reset() { Generator::reset(1); random = 1; } inline void AY8910::NoiseGenerator::doNextEvent() { count = 0; // noise output changes when (bit1 ^ bit0) == 1 output ^= ((random + 1) & 2) >> 1; // The Random Number Generator of the 8910 is a 17-bit shift register. // The input to the shift register is bit0 XOR bit2 (bit0 is the // output). // The following is a fast way to compute bit 17 = bit0^bit2. // Instead of doing all the logic operations, we only check bit 0, // relying on the fact that after two shifts of the register, what now // is bit 2 will become bit 0, and will invert, if necessary, bit 16, // which previously was bit 18. // Note: On Pentium 4, the "if" causes trouble in the pipeline. // After all this is pseudo-random and therefore a nightmare // for branch prediction. // A bit more calculation without a branch is faster. // Without the "if", the transformation described above still // speeds up the code, because the same "random & N" // subexpression appears twice (also when doing multiple cycles // in one go, see "advance" method). // TODO: Benchmark on other modern CPUs. //if (random & 1) random ^= 0x28000; //random >>= 1; random = (random >> 1) ^ ((random & 1) << 14) ^ ((random & 1) << 16); } inline void AY8910::NoiseGenerator::advance(int duration) { assert(count < period); count += duration; int cycles = count / period; count -= cycles * period; // equivalent to count %= period // See advanceToFlip for explanation of noise algorithm. for (; cycles >= 4405; cycles -= 4405) { random ^= (random >> 10) ^ ((random & 0x003FF) << 5) ^ ((random & 0x003FF) << 7); } for (; cycles >= 291; cycles -= 291) { random ^= (random >> 6) ^ ((random & 0x3F) << 9) ^ ((random & 0x3F) << 11); } for (; cycles >= 15; cycles -= 15) { random = (random & 0x07FFF) ^ (random >> 15) ^ ((random & 0x07FFF) << 2); } while (cycles--) { random = (random >> 1) ^ ((random & 1) << 14) ^ ((random & 1) << 16); } output = random & 1; } // Amplitude: static bool checkAY8910(const DeviceConfig& config) { string type = StringOp::toLower(config.getChildData("type", "ay8910")); if (type == "ay8910") { return true; } else if (type == "ym2149") { return false; } else { throw FatalError("Unknown PSG type: " + type); } } AY8910::Amplitude::Amplitude(const DeviceConfig& config) : isAY8910(checkAY8910(config)) { vol[0] = vol[1] = vol[2] = 0; envChan[0] = false; envChan[1] = false; envChan[2] = false; setMasterVolume(32768); } const unsigned* AY8910::Amplitude::getEnvVolTable() const { return envVolTable; } inline unsigned AY8910::Amplitude::getVolume(unsigned chan) const { assert(!followsEnvelope(chan)); return vol[chan]; } inline void AY8910::Amplitude::setChannelVolume(unsigned chan, unsigned value) { envChan[chan] = (value & 0x10) != 0; vol[chan] = volTable[value & 0x0F]; } inline void AY8910::Amplitude::setMasterVolume(int volume) { // Calculate the volume->voltage conversion table. // The AY-3-8910 has 16 levels, in a logarithmic scale (3dB per step). // YM2149 has 32 levels, the 16 extra levels are only used for envelope // volumes double out = volume; // avoid clipping double factor = pow(0.5, 0.25); // 1/sqrt(sqrt(2)) ~= 1/(1.5dB) for (int i = 31; i > 0; --i) { envVolTable[i] = unsigned(out + 0.5); // round to nearest; out *= factor; } envVolTable[0] = 0; volTable[0] = 0; for (int i = 1; i < 16; ++i) { volTable[i] = envVolTable[2 * i + 1]; } if (isAY8910) { // only 16 envelope steps, duplicate every step envVolTable[1] = 0; for (int i = 2; i < 32; i += 2) { envVolTable[i] = envVolTable[i + 1]; } } } inline bool AY8910::Amplitude::followsEnvelope(unsigned chan) const { return envChan[chan]; } // Envelope: // AY8910 and YM2149 behave different here: // YM2149 envelope goes twice as fast and has twice as many levels. Here // we implement the YM2149 behaviour, but to get the AY8910 behaviour we // repeat every level twice in the envVolTable inline AY8910::Envelope::Envelope(const unsigned* envVolTable_) { envVolTable = envVolTable_; period = 1; count = 0; step = 0; attack = 0; hold = false; alternate = false; holding = false; } inline void AY8910::Envelope::reset() { count = 0; } inline void AY8910::Envelope::setPeriod(int value) { // twice as fast as AY8910 // see also Generator::setPeriod() period = std::max(1, 2 * value); count = std::min(count, period - 1); } inline unsigned AY8910::Envelope::getVolume() const { return envVolTable[step ^ attack]; } inline void AY8910::Envelope::setShape(unsigned shape) { // do 32 steps for both AY8910 and YM2149 /* envelope shapes: C AtAlH 0 0 x x \___ 0 1 x x /___ 1 0 0 0 \\\\ 1 0 0 1 \___ 1 0 1 0 \/\/ 1 0 1 1 \ 1 1 0 0 //// 1 1 0 1 / 1 1 1 0 /\/\ 1 1 1 1 /___ */ attack = (shape & 0x04) ? 0x1F : 0x00; if ((shape & 0x08) == 0) { // If Continue = 0, map the shape to the equivalent one // which has Continue = 1. hold = true; alternate = attack != 0; } else { hold = (shape & 0x01) != 0; alternate = (shape & 0x02) != 0; } count = 0; step = 0x1F; holding = false; } inline bool AY8910::Envelope::isChanging() const { return !holding; } inline void AY8910::Envelope::doSteps(int steps) { // For best performance callers should check upfront whether // isChanging() == true // Though we can't assert on it because the condition might change // in the inner loop(s) of generateChannels(). //assert(!holding); if (holding) return; step -= steps; // Check current envelope position. if (step < 0) { if (hold) { if (alternate) attack ^= 0x1F; holding = true; step = 0; } else { // If step has looped an odd number of times // (usually 1), invert the output. if (alternate && (step & 0x10)) { attack ^= 0x1F; } step &= 0x1F; } } } inline void AY8910::Envelope::advance(int duration) { assert(count < period); count += duration * 2; if (count >= period) { int steps = count / period; count -= steps * period; // equivalent to count %= period; doSteps(steps); } } inline void AY8910::Envelope::doNextEvent() { count = 0; doSteps(period == 1 ? 2 : 1); } inline unsigned AY8910::Envelope::getNextEventTime() const { assert(count < period); return (period - count + 1) / 2; } inline void AY8910::Envelope::advanceFast(unsigned duration) { count += 2 * duration; assert(count < period); } // AY8910 main class: AY8910::AY8910(const std::string& name, AY8910Periphery& periphery_, const DeviceConfig& config, EmuTime::param time) : ResampledSoundDevice(config.getMotherBoard(), name, "PSG", 3) , periphery(periphery_) , debuggable(make_unique( config.getMotherBoard(), *this)) , vibratoPercent(make_unique( config.getCommandController(), getName() + "_vibrato_percent", "controls strength of vibrato effect", 0.0, 0.0, 10.0)) , vibratoFrequency(make_unique( config.getCommandController(), getName() + "_vibrato_frequency", "frequency of vibrato effect in Hertz", 5, 1.0, 10.0)) , detunePercent(make_unique( config.getCommandController(), getName() + "_detune_percent", "controls strength of detune effect", 0.0, 0.0, 10.0)) , detuneFrequency(make_unique( config.getCommandController(), getName() + "_detune_frequency", "frequency of detune effect in Hertz", 5.0, 1.0, 100.0)) , directionsCallback(make_unique( config.getGlobalSettings().getInvalidPsgDirectionsSetting())) , amplitude(config) , envelope(amplitude.getEnvVolTable()) , isAY8910(checkAY8910(config)) { // (lazily) initialize detune stuff detuneInitialized = false; update(*vibratoPercent); vibratoPercent->attach(*this); detunePercent ->attach(*this); for (int chan = 0; chan < 3; ++chan) { tone[chan].setParent(*this); } // make valgrind happy memset(regs, 0, sizeof(regs)); setInputRate(NATIVE_FREQ_INT); reset(time); registerSound(config); } AY8910::~AY8910() { unregisterSound(); vibratoPercent->detach(*this); detunePercent ->detach(*this); } void AY8910::reset(EmuTime::param time) { // Reset generators and envelope. for (unsigned chan = 0; chan < 3; ++chan) { tone[chan].reset(0); } noise.reset(); envelope.reset(); // Reset registers and values derived from them. for (unsigned reg = 0; reg <= 15; ++reg) { wrtReg(reg, 0, time); } } byte AY8910::readRegister(unsigned reg, EmuTime::param time) { assert(reg <= 15); switch (reg) { case AY_PORTA: if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input regs[reg] = periphery.readA(time); } break; case AY_PORTB: if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input regs[reg] = periphery.readB(time); } break; } // TODO some AY8910 models have 1F as mask for registers 1, 3, 5 static const byte regMask[16] = { 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x0f, 0x1f, 0xff, 0x1f, 0x1f ,0x1f, 0xff, 0xff, 0x0f, 0xff, 0xff }; return isAY8910 ? regs[reg] & regMask[reg] : regs[reg]; } byte AY8910::peekRegister(unsigned reg, EmuTime::param time) const { assert(reg <= 15); switch (reg) { case AY_PORTA: if (!(regs[AY_ENABLE] & PORT_A_DIRECTION)) { // input return periphery.readA(time); } break; case AY_PORTB: if (!(regs[AY_ENABLE] & PORT_B_DIRECTION)) { // input return periphery.readB(time); } break; } return regs[reg]; } void AY8910::writeRegister(unsigned reg, byte value, EmuTime::param time) { assert(reg <= 15); if ((reg < AY_PORTA) && (reg == AY_ESHAPE || regs[reg] != value)) { // Update the output buffer before changing the register. updateStream(time); } wrtReg(reg, value, time); } void AY8910::wrtReg(unsigned reg, byte value, EmuTime::param time) { // Warn/force port directions if (reg == AY_ENABLE) { if (value & PORT_A_DIRECTION) { directionsCallback->execute(); } // portA -> input // portB -> output value = (value & ~PORT_A_DIRECTION) | PORT_B_DIRECTION; } // Note: unused bits are stored as well; they can be read back. byte oldValue = regs[reg]; regs[reg] = value; switch (reg) { case AY_AFINE: case AY_ACOARSE: case AY_BFINE: case AY_BCOARSE: case AY_CFINE: case AY_CCOARSE: tone[reg / 2].setPeriod(regs[reg & ~1] + 256 * (regs[reg | 1] & 0x0F)); break; case AY_NOISEPER: // half the frequency of tone generation noise.setPeriod(2 * (value & 0x1F)); break; case AY_AVOL: case AY_BVOL: case AY_CVOL: amplitude.setChannelVolume(reg - AY_AVOL, value); break; case AY_EFINE: case AY_ECOARSE: // also half the frequency of tone generation, but handled // inside Envelope::setPeriod() envelope.setPeriod(regs[AY_EFINE] + 256 * regs[AY_ECOARSE]); break; case AY_ESHAPE: envelope.setShape(value); break; case AY_ENABLE: if ((value & PORT_A_DIRECTION) && !(oldValue & PORT_A_DIRECTION)) { // Changed from input to output. periphery.writeA(regs[AY_PORTA], time); } if ((value & PORT_B_DIRECTION) && !(oldValue & PORT_B_DIRECTION)) { // Changed from input to output. periphery.writeB(regs[AY_PORTB], time); } break; case AY_PORTA: if (regs[AY_ENABLE] & PORT_A_DIRECTION) { // output periphery.writeA(value, time); } break; case AY_PORTB: if (regs[AY_ENABLE] & PORT_B_DIRECTION) { // output periphery.writeB(value, time); } break; } } static void addFill(int*& buf, int val, unsigned num) { // Note: in the past we tried to optimize this by always producing // a multiple of 4 output values. In the general case a sounddevice is // allowed to do this, but only at the end of the soundbuffer. This // method can also be called in the middle of a buffer (so multiple // times per buffer), in such case it does go wrong. assert(num > 0); #ifdef __arm__ asm volatile ( "subs %[num],%[num],#4\n\t" "bmi 1f\n" "0:\n\t" "ldmia %[buf],{r3-r6}\n\t" "add r3,r3,%[val]\n\t" "add r4,r4,%[val]\n\t" "add r5,r5,%[val]\n\t" "add r6,r6,%[val]\n\t" "stmia %[buf]!,{r3-r6}\n\t" "subs %[num],%[num],#4\n\t" "bpl 0b\n" "1:\n\t" "tst %[num],#2\n\t" "beq 2f\n\t" "ldmia %[buf],{r3-r4}\n\t" "add r3,r3,%[val]\n\t" "add r4,r4,%[val]\n\t" "stmia %[buf]!,{r3-r4}\n" "2:\n\t" "tst %[num],#1\n\t" "beq 3f\n\t" "ldr r3,[%[buf]]\n\t" "add r3,r3,%[val]\n\t" "str r3,[%[buf]],#4\n" "3:\n\t" : [buf] "=r" (buf) , [num] "=r" (num) : "[buf]" (buf) , [val] "r" (val) , "[num]" (num) : "memory", "r3","r4","r5","r6" ); return; #endif do { *buf++ += val; } while (--num); } void AY8910::generateChannels(int** bufs, unsigned length) { // Disable channels with volume 0: since the sample value doesn't matter, // we can use the fastest path. unsigned chanEnable = regs[AY_ENABLE]; for (unsigned chan = 0; chan < 3; ++chan) { if ((!amplitude.followsEnvelope(chan) && (amplitude.getVolume(chan) == 0)) || (amplitude.followsEnvelope(chan) && !envelope.isChanging() && (envelope.getVolume() == 0))) { bufs[chan] = nullptr; tone[chan].advance(length); chanEnable |= 0x09 << chan; } } // Noise disabled on all channels? if ((chanEnable & 0x38) == 0x38) { noise.advance(length); } // Calculate samples. // The 8910 has three outputs, each output is the mix of one of the // three tone generators and of the (single) noise generator. The two // are mixed BEFORE going into the DAC. The formula to mix each channel // is: // (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable), // where ToneOn and NoiseOn are the current generator state // and ToneDisable and NoiseDisable come from the enable reg. // Note that this means that if both tone and noise are disabled, the // output is 1, not 0, and can be modulated by changing the volume. bool envelopeUpdated = false; Envelope initialEnvelope = envelope; NoiseGenerator initialNoise = noise; for (unsigned chan = 0; chan < 3; ++chan, chanEnable >>= 1) { int* buf = bufs[chan]; if (!buf) continue; ToneGenerator& t = tone[chan]; if (envelope.isChanging() && amplitude.followsEnvelope(chan)) { envelopeUpdated = true; envelope = initialEnvelope; if ((chanEnable & 0x09) == 0x08) { // no noise, square wave: alternating between 0 and 1. unsigned val = t.getOutput() * envelope.getVolume(); unsigned remaining = length; unsigned nextE = envelope.getNextEventTime(); unsigned nextT = t.getNextEventTime(); while ((nextT <= remaining) || (nextE <= remaining)) { if (nextT < nextE) { addFill(buf, val, nextT); remaining -= nextT; nextE -= nextT; envelope.advanceFast(nextT); t.doNextEvent(doDetune); nextT = t.getNextEventTime(); } else if (nextE < nextT) { addFill(buf, val, nextE); remaining -= nextE; nextT -= nextE; t.advanceFast(nextE); envelope.doNextEvent(); nextE = envelope.getNextEventTime(); } else { assert(nextT == nextE); addFill(buf, val, nextT); remaining -= nextT; t.doNextEvent(doDetune); nextT = t.getNextEventTime(); envelope.doNextEvent(); nextE = envelope.getNextEventTime(); } val = t.getOutput() * envelope.getVolume(); } if (remaining) { // last interval (without events) addFill(buf, val, remaining); t.advanceFast(remaining); envelope.advanceFast(remaining); } } else if ((chanEnable & 0x09) == 0x09) { // no noise, channel disabled: always 1. unsigned val = envelope.getVolume(); unsigned remaining = length; unsigned next = envelope.getNextEventTime(); while (next <= remaining) { addFill(buf, val, next); remaining -= next; envelope.doNextEvent(); val = envelope.getVolume(); next = envelope.getNextEventTime(); } if (remaining) { // last interval (without events) addFill(buf, val, remaining); envelope.advanceFast(remaining); } t.advance(length); } else if ((chanEnable & 0x09) == 0x00) { // noise enabled, tone enabled noise = initialNoise; unsigned val = noise.getOutput() * t.getOutput() * envelope.getVolume(); unsigned remaining = length; unsigned nextT = t.getNextEventTime(); unsigned nextN = noise.getNextEventTime(); unsigned nextE = envelope.getNextEventTime(); unsigned next = std::min(std::min(nextT, nextN), nextE); while (next <= remaining) { addFill(buf, val, next); remaining -= next; nextT -= next; nextN -= next; nextE -= next; if (nextT) { t.advanceFast(next); } else { t.doNextEvent(doDetune); nextT = t.getNextEventTime(); } if (nextN) { noise.advanceFast(next); } else { noise.doNextEvent(); nextN = noise.getNextEventTime(); } if (nextE) { envelope.advanceFast(next); } else { envelope.doNextEvent(); nextE = envelope.getNextEventTime(); } next = std::min(std::min(nextT, nextN), nextE); val = noise.getOutput() * t.getOutput() * envelope.getVolume(); } if (remaining) { // last interval (without events) addFill(buf, val, remaining); t.advanceFast(remaining); noise.advanceFast(remaining); envelope.advanceFast(remaining); } } else { // noise enabled, tone disabled noise = initialNoise; unsigned val = noise.getOutput() * envelope.getVolume(); unsigned remaining = length; unsigned nextE = envelope.getNextEventTime(); unsigned nextN = noise.getNextEventTime(); while ((nextN <= remaining) || (nextE <= remaining)) { if (nextN < nextE) { addFill(buf, val, nextN); remaining -= nextN; nextE -= nextN; envelope.advanceFast(nextN); noise.doNextEvent(); nextN = noise.getNextEventTime(); } else if (nextE < nextN) { addFill(buf, val, nextE); remaining -= nextE; nextN -= nextE; noise.advanceFast(nextE); envelope.doNextEvent(); nextE = envelope.getNextEventTime(); } else { assert(nextN == nextE); addFill(buf, val, nextN); remaining -= nextN; noise.doNextEvent(); nextN = noise.getNextEventTime(); envelope.doNextEvent(); nextE = envelope.getNextEventTime(); } val = noise.getOutput() * envelope.getVolume(); } if (remaining) { // last interval (without events) addFill(buf, val, remaining); noise.advanceFast(remaining); envelope.advanceFast(remaining); } t.advance(length); } } else { // no (changing) envelope on this channel unsigned volume = amplitude.followsEnvelope(chan) ? envelope.getVolume() : amplitude.getVolume(chan); if ((chanEnable & 0x09) == 0x08) { // no noise, square wave: alternating between 0 and 1. unsigned val = t.getOutput() * volume; unsigned remaining = length; unsigned next = t.getNextEventTime(); while (next <= remaining) { addFill(buf, val, next); val ^= volume; remaining -= next; t.doNextEvent(doDetune); next = t.getNextEventTime(); } if (remaining) { // last interval (without events) addFill(buf, val, remaining); t.advanceFast(remaining); } } else if ((chanEnable & 0x09) == 0x09) { // no noise, channel disabled: always 1. addFill(buf, volume, length); t.advance(length); } else if ((chanEnable & 0x09) == 0x00) { // noise enabled, tone enabled noise = initialNoise; unsigned val1 = t.getOutput() * volume; unsigned val2 = val1 * noise.getOutput(); unsigned remaining = length; unsigned nextN = noise.getNextEventTime(); unsigned nextT = t.getNextEventTime(); while ((nextN <= remaining) || (nextT <= remaining)) { if (nextT < nextN) { addFill(buf, val2, nextT); remaining -= nextT; nextN -= nextT; noise.advanceFast(nextT); t.doNextEvent(doDetune); nextT = t.getNextEventTime(); val1 ^= volume; val2 = val1 * noise.getOutput(); } else if (nextN < nextT) { addFill(buf, val2, nextN); remaining -= nextN; nextT -= nextN; t.advanceFast(nextN); noise.doNextEvent(); nextN = noise.getNextEventTime(); val2 = val1 * noise.getOutput(); } else { assert(nextT == nextN); addFill(buf, val2, nextT); remaining -= nextT; t.doNextEvent(doDetune); nextT = t.getNextEventTime(); noise.doNextEvent(); nextN = noise.getNextEventTime(); val1 ^= volume; val2 = val1 * noise.getOutput(); } } if (remaining) { // last interval (without events) addFill(buf, val2, remaining); t.advanceFast(remaining); noise.advanceFast(remaining); } } else { // noise enabled, tone disabled noise = initialNoise; unsigned remaining = length; unsigned val = noise.getOutput() * volume; unsigned next = noise.getNextEventTime(); while (next <= remaining) { addFill(buf, val, next); remaining -= next; noise.doNextEvent(); val = noise.getOutput() * volume; next = noise.getNextEventTime(); } if (remaining) { // last interval (without events) addFill(buf, val, remaining); noise.advanceFast(remaining); } t.advance(length); } } } // Envelope not yet updated? if (envelope.isChanging() && !envelopeUpdated) { envelope.advance(length); } } void AY8910::update(const Setting& setting) { if ((&setting == vibratoPercent.get()) || (&setting == detunePercent .get())) { doDetune = (vibratoPercent->getDouble() != 0) || (detunePercent ->getDouble() != 0); if (doDetune && !detuneInitialized) { detuneInitialized = true; initDetune(); } } else { ResampledSoundDevice::update(setting); } } // SimpleDebuggable AY8910Debuggable::AY8910Debuggable(MSXMotherBoard& motherBoard, AY8910& ay8910_) : SimpleDebuggable(motherBoard, ay8910_.getName() + " regs", "PSG", 0x10) , ay8910(ay8910_) { } byte AY8910Debuggable::read(unsigned address, EmuTime::param time) { return ay8910.readRegister(address, time); } void AY8910Debuggable::write(unsigned address, byte value, EmuTime::param time) { return ay8910.writeRegister(address, value, time); } template void AY8910::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("toneGenerators", tone); ar.serialize("noiseGenerator", noise); ar.serialize("envelope", envelope); ar.serialize("registers", regs); // amplitude if (ar.isLoader()) { for (int i = 0; i < 3; ++i) { amplitude.setChannelVolume(i, regs[i + AY_AVOL]); } } } INSTANTIATE_SERIALIZE_METHODS(AY8910); template void AY8910::Generator::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("period", period); ar.serialize("count", count); ar.serialize("output", output); } INSTANTIATE_SERIALIZE_METHODS(AY8910::Generator); template void AY8910::ToneGenerator::serialize(Archive& ar, unsigned version) { ar.template serializeInlinedBase(*this, version); ar.serialize("vibratoCount", vibratoCount); ar.serialize("detuneCount", detuneCount); } INSTANTIATE_SERIALIZE_METHODS(AY8910::ToneGenerator); template void AY8910::NoiseGenerator::serialize(Archive& ar, unsigned version) { ar.template serializeInlinedBase(*this, version); ar.serialize("random", random); } INSTANTIATE_SERIALIZE_METHODS(AY8910::NoiseGenerator); template void AY8910::Envelope::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("period", period); ar.serialize("count", count); ar.serialize("step", step); ar.serialize("attack", attack); ar.serialize("hold", hold); ar.serialize("alternate", alternate); ar.serialize("holding", holding); } INSTANTIATE_SERIALIZE_METHODS(AY8910::Envelope); } // namespace openmsx openmsx-0.10.0/src/sound/ResampleLQ.cc0000644000175000017500000001700612262345041020303 0ustar manuelmanuel00000000000000#include "ResampleLQ.hh" #include "ResampledSoundDevice.hh" #include "likely.hh" #include "memory.hh" #include #include #include namespace openmsx { // 16-byte aligned buffer of ints (shared among all instances of this resampler) static std::vector bufferStorage; // (possibly) unaligned storage static unsigned bufferSize = 0; // usable buffer size (aligned portion) static int* bufferInt = nullptr; // pointer to aligned sub-buffer //// template std::unique_ptr> ResampleLQ::create( ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate) { std::unique_ptr> result; unsigned hostSampleRate = hostClock.getFreq(); if (emuSampleRate < hostSampleRate) { result = make_unique>( input, hostClock, emuSampleRate); } else { result = make_unique>( input, hostClock, emuSampleRate); } return result; } template ResampleLQ::ResampleLQ( ResampledSoundDevice& input_, const DynamicClock& hostClock_, unsigned emuSampleRate) : input(input_) , hostClock(hostClock_) , emuClock(hostClock.getTime(), emuSampleRate) , step(FP::roundRatioDown(emuSampleRate, hostClock.getFreq())) { for (unsigned j = 0; j < 2 * CHANNELS; ++j) { lastInput[j] = 0; } } template bool ResampleLQ::fetchData(EmuTime::param time, unsigned& valid) { unsigned emuNum = emuClock.getTicksTill(time); valid = 2 + emuNum; unsigned required = emuNum + 4; if (unlikely(required > bufferSize)) { // grow buffer (3 extra to be able to align) bufferStorage.resize(required + 3); // align at 16-byte boundary auto p = reinterpret_cast(bufferStorage.data()); bufferInt = reinterpret_cast((p + 15) & ~15); // calculate actual usable size (the aligned portion) bufferSize = &*bufferStorage.end() - bufferInt; assert(bufferSize >= required); } emuClock += emuNum; assert(emuClock.getTime() <= time); assert(emuClock.getFastAdd(1) > time); int* buffer = &bufferInt[4 - 2 * CHANNELS]; assert((long(&buffer[2 * CHANNELS]) & 15) == 0); if (!input.generateInput(&buffer[2 * CHANNELS], emuNum)) { // New input is all zero int last = 0; for (unsigned j = 0; j < 2 * CHANNELS; ++j) { last |= lastInput[j]; } if (last == 0) { // Old input was also all zero, then the resampled // output will be all zero as well. return false; } memset(&buffer[CHANNELS], 0, emuNum * CHANNELS * sizeof(int)); } for (unsigned j = 0; j < 2 * CHANNELS; ++j) { buffer[j] = lastInput[j]; lastInput[j] = buffer[emuNum * CHANNELS + j]; } return true; } //// template ResampleLQUp::ResampleLQUp( ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate) : ResampleLQ(input, hostClock, emuSampleRate) { assert(emuSampleRate < hostClock.getFreq()); // only upsampling } template bool ResampleLQUp::generateOutput( int* __restrict dataOut, unsigned hostNum, EmuTime::param time) { EmuTime host1 = this->hostClock.getFastAdd(1); assert(host1 > this->emuClock.getTime()); FP pos; this->emuClock.getTicksTill(host1, pos); assert(pos.toInt() < 2); unsigned valid; // only indices smaller than this number are valid if (!this->fetchData(time, valid)) return false; // this is currently only used to upsample cassette player sound, // sound quality is not so important here, so use 0-th order // interpolation (instead of 1st-order). int* buffer = &bufferInt[4 - 2 * CHANNELS]; for (unsigned i = 0; i < hostNum; ++i) { unsigned p = pos.toInt(); assert(p < valid); for (unsigned j = 0; j < CHANNELS; ++j) { dataOut[i * CHANNELS + j] = buffer[p * CHANNELS + j]; } pos += this->step; } return true; } //// template ResampleLQDown::ResampleLQDown( ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate) : ResampleLQ(input, hostClock, emuSampleRate) { assert(emuSampleRate > hostClock.getFreq()); // can only do downsampling } template bool ResampleLQDown::generateOutput( int* __restrict dataOut, unsigned hostNum, EmuTime::param time) { EmuTime host1 = this->hostClock.getFastAdd(1); assert(host1 > this->emuClock.getTime()); FP pos; this->emuClock.getTicksTill(host1, pos); unsigned valid; if (!this->fetchData(time, valid)) return false; int* buffer = &bufferInt[4 - 2 * CHANNELS]; #ifdef __arm__ if (CHANNELS == 1) { unsigned dummy; // This asm code is equivalent to the c++ code below (does // 1st order interpolation). It's still a bit slow, so we // use 0th order interpolation. Sound quality is still good // especially on portable devices with only medium quality // speakers. /*asm volatile ( "0:\n\t" "mov r7,%[p],LSR #14\n\t" "add r7,%[buf],r7,LSL #2\n\t" "ldmia r7,{r7,r8}\n\t" "sub r8,r8,r7\n\t" "and %[t],%[p],%[m]\n\t" "mul %[t],r8,%[t]\n\t" "add %[t],r7,%[t],ASR #14\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "mov r7,%[p],LSR #14\n\t" "add r7,%[buf],r7,LSL #2\n\t" "ldmia r7,{r7,r8}\n\t" "sub r8,r8,r7\n\t" "and %[t],%[p],%[m]\n\t" "mul %[t],r8,%[t]\n\t" "add %[t],r7,%[t],ASR #14\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "mov r7,%[p],LSR #14\n\t" "add r7,%[buf],r7,LSL #2\n\t" "ldmia r7,{r7,r8}\n\t" "sub r8,r8,r7\n\t" "and %[t],%[p],%[m]\n\t" "mul %[t],r8,%[t]\n\t" "add %[t],r7,%[t],ASR #14\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "mov r7,%[p],LSR #14\n\t" "add r7,%[buf],r7,LSL #2\n\t" "ldmia r7,{r7,r8}\n\t" "sub r8,r8,r7\n\t" "and %[t],%[p],%[m]\n\t" "mul %[t],r8,%[t]\n\t" "add %[t],r7,%[t],ASR #14\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "subs %[n],%[n],#4\n\t" "bgt 0b\n\t" : [p] "=r" (pos) , [t] "=&r" (dummy) : "[p]" (pos) , [buf] "r" (buffer) , [out] "r" (dataOut) , [s] "r" (step) , [n] "r" (hostNum) , [m] "r" (0x3FFF) // mask 14 bits : "r7","r8" );*/ // 0th order interpolation asm volatile ( "0:\n\t" "lsrs %[t],%[p],#14\n\t" "ldr %[t],[%[buf],%[t],LSL #2]\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "lsrs %[t],%[p],#14\n\t" "ldr %[t],[%[buf],%[t],LSL #2]\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "lsrs %[t],%[p],#14\n\t" "ldr %[t],[%[buf],%[t],LSL #2]\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "lsrs %[t],%[p],#14\n\t" "ldr %[t],[%[buf],%[t],LSL #2]\n\t" "str %[t],[%[out]],#4\n\t" "add %[p],%[p],%[s]\n\t" "subs %[n],%[n],#4\n\t" "bgt 0b\n\t" : [p] "=r" (pos) , [out] "=r" (dataOut) , [n] "=r" (hostNum) , [t] "=&r" (dummy) : "[p]" (pos) , "[out]" (dataOut) , "[n]" (hostNum) , [buf] "r" (buffer) , [s] "r" (this->step) : "memory" ); } else { #endif for (unsigned i = 0; i < hostNum; ++i) { unsigned p = pos.toInt(); assert((p + 1) < valid); FP fract = pos.fract(); for (unsigned j = 0; j < CHANNELS; ++j) { int s0 = buffer[(p + 0) * CHANNELS + j]; int s1 = buffer[(p + 1) * CHANNELS + j]; int out = s0 + (fract * (s1 - s0)).toInt(); dataOut[i * CHANNELS + j] = out; } pos += this->step; } #ifdef __arm__ } #endif return true; } // Force template instantiation. template class ResampleLQ<1>; template class ResampleLQ<2>; } // namespace openmsx openmsx-0.10.0/src/sound/ResampleCoeffs.ii0000644000175000017500000022075712262345041021221 0ustar manuelmanuel00000000000000// Based on libsamplerate-0.1.2 (aka Secret Rabit Code) // see comments in Resample.cc // // f = make_filter(8f, 128f, 100.3); // Pass band width : 0.0039062 (should be 0.0039062) // Stop band atten. : 100.71 dB // -3dB band width : 0.484 // half length : 2463 // increment : 128 8.31472372954840555082e-01f, 8.31414005540308198583e-01f, 8.31238918266223869580e-01f, 8.30947156036480505392e-01f, 8.30538793675450581766e-01f, 8.30013935904800659316e-01f, 8.29372717311066987023e-01f, 8.28615302303967515840e-01f, 8.27741885065490623496e-01f, 8.26752689489751890761e-01f, 8.25647969113678215081e-01f, 8.24428007038499943704e-01f, 8.23093115842108757896e-01f, 8.21643637482293187624e-01f, 8.20079943190897053817e-01f, 8.18402433358933589780e-01f, 8.16611537412689103554e-01f, 8.14707713680854150873e-01f, 8.12691449252757824873e-01f, 8.10563259827706050764e-01f, 8.08323689555523805517e-01f, 8.05973310868314363198e-01f, 8.03512724303517833491e-01f, 8.00942558318331943035e-01f, 7.98263469095534694553e-01f, 7.95476140340800830231e-01f, 7.92581283071560838138e-01f, 7.89579635397499868255e-01f, 7.86471962292734527722e-01f, 7.83259055359786127148e-01f, 7.79941732585400893107e-01f, 7.76520838088307852054e-01f, 7.72997241859018080490e-01f, 7.69371839491718167992e-01f, 7.65645551908390675777e-01f, 7.61819325075220210586e-01f, 7.57894129711408459649e-01f, 7.53870960990470018181e-01f, 7.49750838234153449413e-01f, 7.45534804599028211314e-01f, 7.41223926755909090502e-01f, 7.36819294562192195208e-01f, 7.32322020727209643809e-01f, 7.27733240470738174110e-01f, 7.23054111174766811487e-01f, 7.18285812028632841830e-01f, 7.13429543667664534112e-01f, 7.08486527805442301009e-01f, 7.03458006859804640953e-01f, 6.98345243572719653891e-01f, 6.93149520624175785599e-01f, 6.87872140240182283755e-01f, 6.82514423795047564525e-01f, 6.77077711408058502407e-01f, 6.71563361534684655219e-01f, 6.65972750552474845875e-01f, 6.60307272341742135247e-01f, 6.54568337861228477514e-01f, 6.48757374718860524432e-01f, 6.42875826737744904271e-01f, 6.36925153517562181449e-01f, 6.30906829991492501541e-01f, 6.24822345978837789815e-01f, 6.18673205733470954470e-01f, 6.12460927488293727095e-01f, 6.06187042995817604307e-01f, 5.99853097065060292259e-01f, 5.93460647094893878339e-01f, 5.87011262603992944875e-01f, 5.80506524757569142281e-01f, 5.73948025891025337408e-01f, 5.67337369030688098981e-01f, 5.60676167411809700525e-01f, 5.53966043993961543279e-01f, 5.47208630974010734604e-01f, 5.40405569296826038261e-01f, 5.33558508163880174102e-01f, 5.26669104539922661168e-01f, 5.19739022657876970079e-01f, 5.12769933522119303326e-01f, 5.05763514410336290084e-01f, 4.98721448374081555155e-01f, 4.91645423738241937883e-01f, 4.84537133599546865348e-01f, 4.77398275324308896117e-01f, 4.70230550045545592219e-01f, 4.63035662159660077464e-01f, 4.55815318822846149427e-01f, 4.48571229447379538069e-01f, 4.41305105197960123586e-01f, 4.34018658488283970431e-01f, 4.26713602477997000495e-01f, 4.19391650570203500248e-01f, 4.12054515909689722530e-01f, 4.04703910882034223473e-01f, 3.97341546613763640927e-01f, 3.89969132473721613596e-01f, 3.82588375575806771689e-01f, 3.75200980283257823356e-01f, 3.67808647714624070701e-01f, 3.60413075251609871241e-01f, 3.53015956048925771960e-01f, 3.45618978546330835044e-01f, 3.38223825983006376461e-01f, 3.30832175914426429575e-01f, 3.23445699731881031180e-01f, 3.16066062184803764357e-01f, 3.08694920906066150312e-01f, 3.01333925940378832831e-01f, 2.93984719275965256102e-01f, 2.86648934379644393378e-01f, 2.79328195735489559492e-01f, 2.72024118387182545220e-01f, 2.64738307484245039003e-01f, 2.57472357832259801658e-01f, 2.50227853447243409057e-01f, 2.43006367114305704691e-01f, 2.35809459950733935063e-01f, 2.28638680973647728800e-01f, 2.21495566672345989279e-01f, 2.14381640585498134399e-01f, 2.07298412883298144305e-01f, 2.00247379954717363848e-01f, 1.93230023999986955108e-01f, 1.86247812628430653437e-01f, 1.79302198461779749294e-01f, 1.72394618743085786816e-01f, 1.65526494951356295537e-01f, 1.58699232422028796430e-01f, 1.51914219973401071195e-01f, 1.45172829539132269838e-01f, 1.38476415806921215879e-01f, 1.31826315863480453272e-01f, 1.25223848845901208904e-01f, 1.18670315599523901184e-01f, 1.12166998342411894374e-01f, 1.05715160336527447260e-01f, 9.93160455657086521652e-02f, 9.29708784205405536216e-02f, 8.66808633902153846673e-02f, 8.04471847614677826321e-02f, 7.42710063246745516574e-02f, 6.81534710872001986415e-02f, 6.20957009940759641076e-02f, 5.60987966560835549235e-02f, 5.01638370853247708703e-02f, 4.42918794383505357026e-02f, 3.84839587669171534490e-02f, 3.27410877764400740086e-02f, 2.70642565922108620236e-02f, 2.14544325334371267788e-02f, 1.59125598951669576520e-02f, 1.04395597381551803740e-02f, 5.03632968672305773861e-03f, -2.96256265336385191805e-04f, -5.55734794075828358179e-03f, -1.07461191566687631893e-02f, -1.58617678942645466689e-02f, -2.09035164602743607498e-02f, -2.58706116401622790435e-02f, -3.07623248430414844568e-02f, -3.55779522382659724178e-02f, -4.03168148836769782428e-02f, -4.49782588454727128013e-02f, -4.95616553096875425699e-02f, -5.40664006852556791594e-02f, -5.84919166986474642345e-02f, -6.28376504800633867154e-02f, -6.71030746411782619276e-02f, -7.12876873444269476554e-02f, -7.53910123638282386738e-02f, -7.94125991373483691715e-02f, -8.33520228108008270906e-02f, -8.72088842732959695914e-02f, -9.09828101842390379872e-02f, -9.46734529918955292072e-02f, -9.82804909435327500589e-02f, -1.01803628087157427284e-01f, -1.05242594264867719844e-01f, -1.08597145097841310535e-01f, -1.11867061962988789681e-01f, -1.15052151961296145188e-01f, -1.18152247877890054228e-01f, -1.21167208133862752684e-01f, -1.24096916729885473063e-01f, -1.26941283181660202750e-01f, -1.29700242447243679900e-01f, -1.32373754846295377252e-01f, -1.34961805971292009287e-01f, -1.37464406590764143257e-01f, -1.39881592544604443917e-01f, -1.42213424631507739937e-01f, -1.44459988488595730827e-01f, -1.46621394463294696386e-01f, -1.48697777477524800682e-01f, -1.50689296884269657850e-01f, -1.52596136316595465399e-01f, -1.54418503529190731527e-01f, -1.56156630232500315270e-01f, -1.57810771919529219121e-01f, -1.59381207685401427021e-01f, -1.60868240039743037872e-01f, -1.62272194711985145998e-01f, -1.63593420449666626659e-01f, -1.64832288809824062392e-01f, -1.65989193943563151379e-01f, -1.67064552373901109572e-01f, -1.68058802766975601273e-01f, -1.68972405696717037360e-01f, -1.69805843403086798027e-01f, -1.70559619543971530131e-01f, -1.71234258940853617537e-01f, -1.71830307318344255307e-01f, -1.72348331037702334756e-01f, -1.72788916824434257702e-01f, -1.73152671490098081231e-01f, -1.73440221648409775845e-01f, -1.73652213425782242506e-01f, -1.73789312166397952319e-01f, -1.73852202131942051855e-01f, -1.73841586196111674845e-01f, -1.73758185534021086793e-01f, -1.73602739306629005878e-01f, -1.73376004340306061335e-01f, -1.73078754801670009478e-01f, -1.72711781867818603420e-01f, -1.72275893392080048372e-01f, -1.71771913565416961545e-01f, -1.71200682573611373538e-01f, -1.70563056250360139954e-01f, -1.69859905726417126370e-01f, -1.69092117074913228514e-01f, -1.68260590952989147473e-01f, -1.67366242239875284703e-01f, -1.66409999671557895518e-01f, -1.65392805472166642966e-01f, -1.64315614982222552021e-01f, -1.63179396283883837437e-01f, -1.61985129823331186483e-01f, -1.60733808030429803360e-01f, -1.59426434935813571281e-01f, -1.58064025785527417778e-01f, -1.56647606653372045704e-01f, -1.55178214051094831571e-01f, -1.53656894536566474008e-01f, -1.52084704320088470730e-01f, -1.50462708868975059140e-01f, -1.48791982510548842500e-01f, -1.47073608033699704256e-01f, -1.45308676289147314931e-01f, -1.43498285788550977715e-01f, -1.41643542302611558092e-01f, -1.39745558458309881988e-01f, -1.37805453335422323224e-01f, -1.35824352062461073398e-01f, -1.33803385412180564362e-01f, -1.31743689396791985313e-01f, -1.29646404863030306753e-01f, -1.27512677087215337002e-01f, -1.25343655370452389253e-01f, -1.23140492634104758984e-01f, -1.20904345015691472298e-01f, -1.18636371465341922127e-01f, -1.16337733342949820048e-01f, -1.14009594016166518338e-01f, -1.11653118459372716065e-01f, -1.09269472853762789066e-01f, -1.06859824188683741331e-01f, -1.04425339864360325337e-01f, -1.01967187296145456177e-01f, -9.94865335204263567803e-02f, -9.69845448023236023083e-02f, -9.44623862453117940641e-02f, -9.19212214028948121358e-02f, -8.93622118924671249296e-02f, -8.67865170114848205607e-02f, -8.41952933560805999447e-02f, -8.15896944422443981537e-02f, -7.89708703296961439522e-02f, -7.63399672485739477779e-02f, -7.36981272290610500697e-02f, -7.10464877340710454501e-02f, -6.83861812951113146042e-02f, -6.57183351514422919859e-02f, -6.30440708926501142129e-02f, -6.03645041047437408421e-02f, -5.76807440198948140342e-02f, -5.49938931699267691267e-02f, -5.23050470436661057994e-02f, -4.96152937482609926456e-02f, -4.69257136745778041798e-02f, -4.42373791667729082677e-02f, -4.15513541961495605492e-02f, -3.88686940393953503370e-02f, -3.61904449613011935938e-02f, -3.35176439020573244121e-02f, -3.08513181692228674602e-02f, -2.81924851344595717162e-02f, -2.55421519351213023585e-02f, -2.29013151807887539724e-02f, -2.02709606648342685609e-02f, -1.76520630811025022733e-02f, -1.50455857457888787787e-02f, -1.24524803245954687053e-02f, -9.87368656524285036313e-03f, -7.31013203541311037958e-03f, -4.76273186619807602227e-03f, -2.23238850112297869746e-03f, 2.80008549183706099625e-04f, 2.77358294660976899965e-03f, 5.24747175940274562800e-03f, 7.70082569017439908660e-03f, 1.01328092980087648006e-02f, 1.25426012146140665460e-02f, 1.49293943544662570388e-02f, 1.72923961188884665885e-02f, 1.96308285940195309527e-02f, 2.19439287426209730936e-02f, 2.42309485896793734561e-02f, 2.64911554017603391442e-02f, 2.87238318600733545660e-02f, 3.09282762272103349532e-02f, 3.31038025075217068327e-02f, 3.52497406010981520486e-02f, 3.73654364513253609004e-02f, 3.94502521859858221176e-02f, 4.15035662518817155542e-02f, 4.35247735429537541130e-02f, 4.55132855218787699125e-02f, 4.74685303351244439196e-02f, 4.93899529214478216765e-02f, 5.12770151138242716304e-02f, 5.31291957347935772660e-02f, 5.49459906852194576721e-02f, 5.67269130264521220797e-02f, 5.84714930558940249039e-02f, 6.01792783759655322551e-02f, 6.18498339564735599705e-02f, 6.34827421903864652641e-02f, 6.50776029430226859995e-02f, 6.66340335946605799577e-02f, 6.81516690765814614483e-02f, 6.96301619005592065115e-02f, 7.10691821818139612965e-02f, 7.24684176554465098175e-02f, 7.38275736863740761340e-02f, 7.51463732727930683319e-02f, 7.64245570431912463194e-02f, 7.76618832469397474272e-02f, 7.88581277384926976337e-02f, 8.00130839552289779837e-02f, 8.11265628889681067459e-02f, 8.21983930512013155623e-02f, 8.32284204320703352442e-02f, 8.42165084531432683868e-02f, 8.51625379140240473808e-02f, 8.60664069328434949702e-02f, 8.69280308806818224898e-02f, 8.77473423099686122839e-02f, 8.85242908769151987114e-02f, 8.92588432580306151420e-02f, 8.99509830607803234637e-02f, 9.06007107284422380511e-02f, 9.12080434392217309636e-02f, 9.17730149996878741270e-02f, 9.22956757325926607782e-02f, 9.27760923591415126443e-02f, 9.32143478757788968014e-02f, 9.36105414255621187669e-02f, 9.39647881641913207407e-02f, 9.42772191207702781046e-02f, 9.45479810533706027664e-02f, 9.47772362994778183598e-02f, 9.49651626213951355338e-02f, 9.51119530466846413441e-02f, 9.52178157037280176178e-02f, 9.52829736524876819148e-02f, 9.53076647105531166160e-02f, 9.52921412745576373871e-02f, 9.52366701370536278271e-02f, 9.51415322989309503177e-02f, 9.50070227774735681647e-02f, 9.48334504101390751707e-02f, 9.46211376541590265532e-02f, 9.43704203820504156086e-02f, 9.40816476731309581094e-02f, 9.37551816011396865758e-02f, 9.33913970180541563870e-02f, 9.29906813342047527948e-02f, 9.25534342947849225647e-02f, 9.20800677528557931506e-02f, 9.15710054389489019888e-02f, 9.10266827273659706599e-02f, 9.04475463992783224043e-02f, 8.98340544027328158361e-02f, 8.91866756096650198371e-02f, 8.85058895700238101867e-02f, 8.77921862631190763615e-02f, 8.70460658462897246546e-02f, 8.62680384010083983748e-02f, 8.54586236765221690659e-02f, 8.46183508311429133375e-02f, 8.37477581712920277068e-02f, 8.28473928884114751980e-02f, 8.19178107938471483651e-02f, 8.09595760518180135312e-02f, 7.99732609105757996648e-02f, 7.89594454318716387764e-02f, 7.79187172188340326784e-02f, 7.68516711423724852015e-02f, 7.57589090662164482692e-02f, 7.46410395707000073884e-02f, 7.34986776754032733461e-02f, 7.23324445607601979047e-02f, 7.11429672887474440213e-02f, 6.99308785227581580779e-02f, 6.86968162467783832748e-02f, 6.74414234839716131287e-02f, 6.61653480147834510694e-02f, 6.48692420946761771905e-02f, 6.35537621716019962559e-02f, 6.22195686033254202751e-02f, 6.08673253747022482973e-02f, 5.94976998150253330588e-02f, 5.81113623155428762890e-02f, 5.67089860472591994478e-02f, 5.52912466791220663653e-02f, 5.38588220967053943333e-02f, 5.24123921214928872869e-02f, 5.09526382308646275110e-02f, 4.94802432788957607945e-02f, 4.79958912180662375380e-02f, 4.65002668219884549017e-02f, 4.49940554092515265783e-02f, 4.34779425684853407241e-02f, 4.19526138847447563340e-02f, 4.04187546673120054463e-02f, 3.88770496790168534895e-02f, 3.73281828671714888124e-02f, 3.57728370962169389680e-02f, 3.42116938821758476141e-02f, 3.26454331290065291604e-02f, 3.10747328669506231447e-02f, 2.95002689929673225788e-02f, 2.79227150133440210622e-02f, 2.63427417885741359249e-02f, 2.47610172805882329528e-02f, 2.31782063024293799591e-02f, 2.15949702704538760989e-02f, 2.00119669591453143431e-02f, 1.84298502586232419709e-02f, 1.68492699349288496680e-02f, 1.52708713931675090641e-02f, 1.36952954435869880129e-02f, 1.21231780706691841254e-02f, 1.05551502053105091677e-02f, 8.99183750016553651196e-03f, 7.43386010822696258193e-03f, 5.88183246471273707412e-03f, 4.33636307232945251988e-03f, 2.79805428998205086427e-03f, 1.26750212499337003291e-03f, -2.54703971099550386531e-04f, -1.76798130311027175757e-03f, -3.27175412906725469539e-03f, -4.76545385331804925710e-03f, -6.24851921581533794464e-03f, -7.72039647752874400727e-03f, -9.18053960192777122884e-03f, -1.06284104324833178490e-02f, -1.20634788661366718077e-02f, -1.34852230226875247771e-02f, -1.48931294100519973078e-02f, -1.62866930853476296615e-02f, -1.76654178117594401476e-02f, -1.90288162111466874205e-02f, -2.03764099123495759369e-02f, -2.17077296951579609696e-02f, -2.30223156299061669505e-02f, -2.43197172126588360974e-02f, -2.55994934959561624976e-02f, -2.68612132150869431513e-02f, -2.81044549098614510063e-02f, -2.93288070418574950415e-02f, -3.05338681071131295974e-02f, -3.17192467442452205595e-02f, -3.28845618379712614776e-02f, -3.40294426180154721551e-02f, -3.51535287533818185945e-02f, -3.62564704419792716017e-02f, -3.73379284955845242022e-02f, -3.83975744201309962533e-02f, -3.94350904913155775322e-02f, -4.04501698255130062720e-02f, -4.14425164459938585870e-02f, -4.24118453444415760556e-02f, -4.33578825377650758921e-02f, -4.42803651202084772032e-02f, -4.51790413107587551789e-02f, -4.60536704958539877541e-02f, -4.69040232673985507672e-02f, -4.77298814560914094751e-02f, -4.85310381600771723054e-02f, -4.93072977689298017068e-02f, -5.00584759829825892696e-02f, -5.07843998280173986037e-02f, -5.14849076653303427964e-02f, -5.21598491971914657306e-02f, -5.28090854677170859488e-02f, -5.34324888591782357072e-02f, -5.40299430837655400572e-02f, -5.46013431708381041796e-02f, -5.51465954496810906171e-02f, -5.56656175277993395256e-02f, -5.61583382647804357779e-02f, -5.66246977417538960298e-02f, -5.70646472264832865795e-02f, -5.74781491341238848225e-02f, -5.78651769836829588112e-02f, -5.82257153502198851469e-02f, -5.85597598128258789441e-02f, -5.88673168984241990120e-02f, -5.91484040214318093631e-02f, -5.94030494193287308957e-02f, -5.96312920841784027681e-02f, -5.98331816901454746627e-02f, -6.00087785170606569096e-02f, -6.01581533700810480725e-02f, -6.02813874954959694197e-02f, -6.03785724927326447609e-02f, -6.04498102226119424230e-02f, -6.04952127119116611631e-02f, -6.05149020542914278797e-02f, -6.05090103076376881197e-02f, -6.04776793878847099273e-02f, -6.04210609593744951695e-02f, -6.03393163218124903291e-02f, -6.02326162938837256222e-02f, -6.01011410935896536745e-02f, -5.99450802153716350018e-02f, -5.97646323040843391317e-02f, -5.95600050258849322837e-02f, -5.93314149361059764431e-02f, -5.90790873441773764507e-02f, -5.88032561756684640786e-02f, -5.85041638315173181950e-02f, -5.81820610445198463379e-02f, -5.78372067331465664064e-02f, -5.74698678527617162759e-02f, -5.70803192443151696800e-02f, -5.66688434805820984153e-02f, -5.62357307100216502471e-02f, -5.57812784983319834287e-02f, -5.53057916677746758127e-02f, -5.48095821343453915020e-02f, -5.42929687428649263015e-02f, -5.37562771000702349644e-02f, -5.31998394057807341695e-02f, -5.26239942822169029513e-02f, -5.20290866015511582754e-02f, -5.14154673117670768523e-02f, -5.07834932609073572141e-02f, -5.01335270197884388943e-02f, -4.94659367032617980353e-02f, -4.87810957901005926018e-02f, -4.80793829415919610204e-02f, -4.73611818189140221236e-02f, -4.66268808993793651418e-02f, -4.58768732916221277929e-02f, -4.51115565498113532672e-02f, -4.43313324869706107401e-02f, -4.35366069874822472774e-02f, -4.27277898188581847783e-02f, -4.19052944428566706558e-02f, -4.10695378260253277092e-02f, -4.02209402497498702544e-02f, -3.93599251198885058400e-02f, -3.84869187760717781921e-02f, -3.76023503007467674308e-02f, -3.67066513280452297319e-02f, -3.58002558525536487832e-02f, -3.48836000380640318119e-02f, -3.39571220263849699039e-02f, -3.30212617462878818553e-02f, -3.20764607226682249563e-02f, -3.11231618859974003277e-02f, -3.01618093821427596390e-02f, -2.91928483826300218251e-02f, -2.82167248954252464221e-02f, -2.72338855763107207109e-02f, -2.62447775409285488646e-02f, -2.52498481775659533444e-02f, -2.42495449607560524530e-02f, -2.32443152657647901516e-02f, -2.22346061840382018537e-02f, -2.12208643396787077773e-02f, -2.02035357070221716080e-02f, -1.91830654293842946256e-02f, -1.81598976390459701524e-02f, -1.71344752785447841659e-02f, -1.61072399233397958729e-02f, -1.50786316059164128556e-02f, -1.40490886413957953571e-02f, -1.30190474547137412242e-02f, -1.19889424094323342185e-02f, -1.09592056382471266657e-02f, -9.93026687525074697183e-03f, -8.90255329001433948211e-03f, -7.87648932354562125724e-03f, -6.85249652618241146540e-03f, -5.83099339747908569642e-03f, -4.81239522814202146106e-03f, -3.79711394406930576734e-03f, -2.78555795254968683455e-03f, -1.77813199067227692071e-03f, -7.75236976000132386663e-04f, 2.22730140442126654798e-04f, 1.21537651881706244492e-03f, 2.20231357271108733539e-03f, 3.18315710891246220898e-03f, 4.15752746468348553799e-03f, 5.12504964248380791986e-03f, 6.08535344210042478813e-03f, 7.03807359014245199208e-03f, 7.98284986685961206465e-03f, 8.91932723024580452476e-03f, 9.84715593738785290034e-03f, 1.07659916630240357766e-02f, 1.16754956152756248638e-02f, 1.25753346485176220604e-02f, 1.34651813733560731662e-02f, 1.43447142636787781933e-02f, 1.52136177607511777904e-02f, 1.60715823743268690360e-02f, 1.69183047807457617728e-02f, 1.77534879179936204430e-02f, 1.85768410776981605925e-02f, 1.93880799940382604618e-02f, 2.01869269295435888045e-02f, 2.09731107577651766649e-02f, 2.17463670427963037812e-02f, 2.25064381156266125894e-02f, 2.32530731473125917841e-02f, 2.39860282189490944815e-02f, 2.47050663884288181082e-02f, 2.54099577539762186418e-02f, 2.61004795144461655687e-02f, 2.67764160263764816605e-02f, 2.74375588577874841845e-02f, 2.80837068387202806741e-02f, 2.87146661085097808230e-02f, 2.93302501597869115513e-02f, 2.99302798792087168533e-02f, 3.05145835849139068774e-02f, 3.10829970607048658437e-02f, 3.16353635869560598226e-02f, 3.21715339682534032240e-02f, 3.26913665577675052742e-02f, 3.31947272783659833029e-02f, 3.36814896404726560331e-02f, 3.41515347566807569990e-02f, 3.46047513531298478462e-02f, 3.50410357776568884280e-02f, 3.54602920047340924858e-02f, 3.58624316372060172875e-02f, 3.62473739048404727803e-02f, 3.66150456597097023748e-02f, 3.69653813684179058385e-02f, 3.72983231011940682964e-02f, 3.76138205178691634178e-02f, 3.79118308507581658340e-02f, 3.81923188844700278732e-02f, 3.84552569326661666804e-02f, 3.87006248117945095277e-02f, 3.89284098118221136287e-02f, 3.91386066639944005252e-02f, 3.93312175056476295842e-02f, 3.95062518421033306848e-02f, 3.96637265056755394799e-02f, 3.98036656118202977761e-02f, 3.99261005124597820326e-02f, 4.00310697465144360585e-02f, 4.01186189876763035778e-02f, 4.01888009894591641258e-02f, 4.02416755275608953313e-02f, 4.02773093395744422041e-02f, 4.02957760620868618573e-02f, 4.02971561652026855072e-02f, 4.02815368845340013304e-02f, 4.02490121506946865737e-02f, 4.01996825163432602857e-02f, 4.01336550808131173329e-02f, 4.00510434123766412284e-02f, 3.99519674681838021790e-02f, 3.98365535119223901361e-02f, 3.97049340292425986809e-02f, 3.95572476409943238340e-02f, 3.93936390143226622396e-02f, 3.92142587716682866628e-02f, 3.90192633977227906761e-02f, 3.88088151443859719070e-02f, 3.85830819337740632546e-02f, 3.83422372593309676581e-02f, 3.80864600850902706997e-02f, 3.78159347431409609275e-02f, 3.75308508293468318096e-02f, 3.72314030973733209318e-02f, 3.69177913510723085255e-02f, 3.65902203352790472701e-02f, 3.62488996250740352911e-02f, 3.58940435135636018438e-02f, 3.55258708982338911042e-02f, 3.51446051659309519066e-02f, 3.47504740765239503175e-02f, 3.43437096453047957523e-02f, 3.39245480241803926136e-02f, 3.34932293817127510471e-02f, 3.30499977820627663383e-02f, 3.25951010628938789293e-02f, 3.21287907122915217251e-02f, 3.16513217447548164674e-02f, 3.11629525763171093267e-02f, 3.06639448988514501382e-02f, 3.01545635536184866710e-02f, 2.96350764041116987446e-02f, 2.91057542082603579181e-02f, 2.85668704900414009706e-02f, 2.80187014105628129368e-02f, 2.74615256386703497637e-02f, 2.68956242211381771345e-02f, 2.63212804524964143205e-02f, 2.57387797445546746833e-02f, 2.51484094956766456030e-02f, 2.45504589598617914414e-02f, 2.39452191156906725455e-02f, 2.33329825351894608321e-02f, 2.27140432526683408443e-02f, 2.20886966335908999093e-02f, 2.14572392435271874778e-02f, 2.08199687172471933905e-02f, 2.01771836280079629178e-02f, 1.95291833570884962312e-02f, 1.88762679636269269101e-02f, 1.82187380548123403767e-02f, 1.75568946564845403124e-02f, 1.68910390841945853846e-02f, 1.62214728147774996103e-02f, 1.55484973584896369464e-02f, 1.48724141317607399387e-02f, 1.41935243306124080076e-02f, 1.35121288047925294795e-02f, 1.28285279326754275003e-02f, 1.21430214969758445281e-02f, 1.14559085613274869858e-02f, 1.07674873477713456404e-02f, 1.00780551152029641815e-02f, 9.38790803882408146641e-03f, 8.69734109064560119429e-03f, 8.00664792108640895052e-03f, 7.31612074171312902482e-03f, 6.62605020916498532735e-03f, 5.93672531030635993593e-03f, 5.24843324865020312286e-03f, 4.56145933209378684481e-03f, 3.87608686200798923521e-03f, 3.19259702372048361982e-03f, 2.51126877843176705626e-03f, 1.83237875660391988202e-03f, 1.15620115285868549186e-03f, 4.83007622422852007059e-04f, -1.86932820843070034112e-04f, -8.53353904797455329115e-04f, -1.51599219771675255281e-03f, -2.17458720530792556924e-03f, -2.82888146600037857989e-03f, -3.47862064448672828401e-03f, -4.12355362347965707925e-03f, -4.76343259365718217635e-03f, -5.39801314176371720144e-03f, -6.02705433684159932323e-03f, -6.65031881456398799024e-03f, -7.26757285964317947813e-03f, -7.87858648628854928153e-03f, -8.48313351669007821576e-03f, -9.08099165750268083608e-03f, -9.67194257431004678072e-03f, -1.02557719640449674509e-02f, -1.08322696253466653482e-02f, -1.14012295268339416271e-02f, -1.19624498732761111452e-02f, -1.25157331696445651287e-02f, -1.30608862830260651078e-02f, -1.35977205023845738180e-02f, -1.41260515961539080687e-02f, -1.46456998676501564532e-02f, -1.51564902082884610246e-02f, -1.56582521485937077588e-02f, -1.61508199069943896020e-02f, -1.66340324363880263936e-02f, -1.71077334684716746149e-02f, -1.75717715558275228149e-02f, -1.80260001117568194329e-02f, -1.84702774478586080609e-02f, -1.89044668093441003975e-02f, -1.93284364080869922042e-02f, -1.97420594534034529732e-02f, -2.01452141805614354242e-02f, -2.05377838770183090977e-02f, -2.09196569063852221004e-02f, -2.12907267301215390176e-02f, -2.16508919269584217127e-02f, -2.20000562100566773860e-02f, -2.23381284419012192399e-02f, -2.26650226469371808558e-02f, -2.29806580219539050014e-02f, -2.32849589442222955349e-02f, -2.35778549773940013234e-02f, -2.38592808751701725145e-02f, -2.41291765827496146324e-02f, -2.43874872360661625048e-02f, -2.46341631588262027774e-02f, -2.48691598573592027865e-02f, -2.50924380132932847709e-02f, -2.53039634740697960691e-02f, -2.55037072413113186098e-02f, -2.56916454570593408291e-02f, -2.58677593878966008423e-02f, -2.60320354069717534162e-02f, -2.61844649739453247395e-02f, -2.63250446128731642459e-02f, -2.64537758880496950975e-02f, -2.65706653778289558776e-02f, -2.66757246464459155111e-02f, -2.67689702138592805492e-02f, -2.68504235236379437679e-02f, -2.69201109089152179621e-02f, -2.69780635564342181898e-02f, -2.70243174687087896191e-02f, -2.70589134243261995871e-02f, -2.70818969364167577707e-02f, -2.70933182093176481986e-02f, -2.70932320934577017257e-02f, -2.70816980384915410862e-02f, -2.70587800447114543156e-02f, -2.70245466127663376554e-02f, -2.69790706917171427270e-02f, -2.69224296254590607369e-02f, -2.68547050975419879237e-02f, -2.67759830744198866481e-02f, -2.66863537471611969587e-02f, -2.65859114716531889921e-02f, -2.64747547073322930800e-02f, -2.63529859544745573285e-02f, -2.62207116900796607939e-02f, -2.60780423023825730366e-02f, -2.59250920240284947471e-02f, -2.57619788639449828760e-02f, -2.55888245379471308827e-02f, -2.54057543981124761556e-02f, -2.52128973609604678519e-02f, -2.50103858344739478359e-02f, -2.47983556439997539222e-02f, -2.45769459570643403201e-02f, -2.43462992071435090080e-02f, -2.41065610164222128564e-02f, -2.38578801175844575078e-02f, -2.36004082746693114037e-02f, -2.33343002030331689300e-02f, -2.30597134884559483436e-02f, -2.27768085054302904524e-02f, -2.24857483346725776918e-02f, -2.21866986798954189675e-02f, -2.18798277838799307138e-02f, -2.15653063438876642366e-02f, -2.12433074264517691987e-02f, -2.09140063815867055519e-02f, -2.05775807564556566243e-02f, -2.02342102085360346642e-02f, -1.98840764183222142025e-02f, -1.95273630016047500257e-02f, -1.91642554213670816832e-02f, -1.87949408993371563925e-02f, -1.84196083272362247374e-02f, -1.80384481777610752862e-02f, -1.76516524153425696797e-02f, -1.72594144067167720724e-02f, -1.68619288313498413845e-02f, -1.64593915917550098760e-02f, -1.60519997237402040069e-02f, -1.56399513066264282679e-02f, -1.52234453734734331148e-02f, -1.48026818213531103502e-02f, -1.43778613217079923037e-02f, -1.39491852308316760523e-02f, -1.35168555005115483686e-02f, -1.30810745888681710658e-02f, -1.26420453714316226301e-02f, -1.21999710524887047813e-02f, -1.17550550767402828961e-02f, -1.13075010413035727252e-02f, -1.08575126080952908542e-02f, -1.04052934166326063736e-02f, -9.95104699728536351566e-03f, -9.49497668501652312967e-03f, -9.03728553364356763933e-03f, -8.57817623065582068875e-03f, -8.11785101262214349449e-03f, -7.65651158122056946231e-03f, -7.19435901992488725798e-03f, -6.73159371137851351291e-03f, -6.26841525548942068990e-03f, -5.80502238827697216589e-03f, -5.34161290150089295564e-03f, -4.87838356310490647849e-03f, -4.41553003850264462471e-03f, -3.95324681273798422126e-03f, -3.49172711354636287548e-03f, -3.03116283534747218975e-03f, -2.57174446419663202748e-03f, -2.11366100372138449731e-03f, -1.65709990207213789248e-03f, -1.20224697991074881177e-03f, -7.49286359465203312402e-04f, -2.98400394673150758020e-04f, 1.50230397559290287587e-04f, 5.96427404960260163468e-04f, 1.04001398633389997676e-03f, 1.48081553681653948010e-03f, 1.91865955192711671630e-03f, 2.35337569038958404136e-03f, 2.78479583570576333731e-03f, 3.21275415646031688166e-03f, 3.63708716533605539573e-03f, 4.05763377682291995208e-03f, 4.47423536360066955581e-03f, 4.88673581157838838457e-03f, 5.29498157357465894235e-03f, 5.69882172162047926506e-03f, 6.09810799787139853900e-03f, 6.49269486411187517899e-03f, 6.88243954983998491859e-03f, 7.26720209891677272618e-03f, 7.64684541476874993227e-03f, 8.02123530413159993580e-03f, 8.39024051932213063565e-03f, 8.75373279902990839019e-03f, 9.11158690761618844656e-03f, 9.46368067291306243327e-03f, 9.80989502251233651264e-03f, 1.01501140185368699670e-02f, 1.04842248908878447194e-02f, 1.08121180689596009528e-02f, 1.11336872118183785596e-02f, 1.14488292368375710328e-02f, 1.17574443467867335855e-02f, 1.20594360553697797084e-02f, 1.23547112112087492664e-02f, 1.26431800202723137322e-02f, 1.29247560667452802280e-02f, 1.31993563323394361153e-02f, 1.34669012140451026943e-02f, 1.37273145403230718842e-02f, 1.39805235857388930609e-02f, 1.42264590840399576116e-02f, 1.44650552396788801418e-02f, 1.46962497377853603536e-02f, 1.49199837525900817770e-02f, 1.51362019543059365262e-02f, 1.53448525144697818512e-02f, 1.55458871097522988158e-02f, 1.57392609242401407266e-02f, 1.59249326501989980909e-02f, 1.61028644873237487822e-02f, 1.62730221404839558996e-02f, 1.64353748159745995105e-02f, 1.65898952162792344411e-02f, 1.67365595333573702330e-02f, 1.68753474404654685292e-02f, 1.70062420825228405308e-02f, 1.71292300650343690127e-02f, 1.72443014415816948948e-02f, 1.73514496998961910423e-02f, 1.74506717465267233158e-02f, 1.75419678901157470585e-02f, 1.76253418232991503067e-02f, 1.77008006032431768062e-02f, 1.77683546308354950449e-02f, 1.78280176285450023266e-02f, 1.78798066169677284665e-02f, 1.79237418900749095885e-02f, 1.79598469891815541721e-02f, 1.79881486756524357207e-02f, 1.80086769023645003329e-02f, 1.80214647839439801036e-02f, 1.80265485657978320744e-02f, 1.80239675919585257136e-02f, 1.80137642717629609113e-02f, 1.79959840453853894826e-02f, 1.79706753482452019632e-02f, 1.79378895743111561878e-02f, 1.78976810383233188306e-02f, 1.78501069369546815080e-02f, 1.77952273089348571300e-02f, 1.77331049941585293384e-02f, 1.76638055918014250101e-02f, 1.75873974174670689996e-02f, 1.75039514593883366311e-02f, 1.74135413337067820883e-02f, 1.73162432388551425222e-02f, 1.72121359090659648006e-02f, 1.71013005670323306462e-02f, 1.69838208757447130248e-02f, 1.68597828895295613616e-02f, 1.67292750043147309125e-02f, 1.65923879071472879509e-02f, 1.64492145249898746862e-02f, 1.62998499728209574056e-02f, 1.61443915010654574782e-02f, 1.59829384423819872985e-02f, 1.58155921578329479449e-02f, 1.56424559824643004402e-02f, 1.54636351703211580993e-02f, 1.52792368389266484952e-02f, 1.50893699132506348831e-02f, 1.48941450691946284529e-02f, 1.46936746766213478105e-02f, 1.44880727419542387757e-02f, 1.42774548503756936596e-02f, 1.40619381076500047506e-02f, 1.38416410815988405458e-02f, 1.36166837432563775367e-02f, 1.33871874077307433104e-02f, 1.31532746747999255282e-02f, 1.29150693692685249875e-02f, 1.26726964811125480254e-02f, 1.24262821054400597609e-02f, 1.21759533822933443264e-02f, 1.19218384363212748234e-02f, 1.16640663163469111840e-02f, 1.14027669348586990772e-02f, 1.11380710074510391738e-02f, 1.08701099922405512027e-02f, 1.05990160292857588803e-02f, 1.03249218800347264402e-02f, 1.00479608668283364181e-02f, 9.76826681248407595326e-03f, 9.48597397998680001707e-03f, 9.20121701231205180171e-03f, 8.91413087240663405686e-03f, 8.62485078335300560382e-03f, 8.33351216874106057175e-03f, 8.04025059327335284154e-03f, 7.74520170362733365033e-03f, 7.44850116959968472363e-03f, 7.15028462555652392224e-03f, 6.85068761221313375642e-03f, 6.54984551876693164157e-03f, 6.24789352540736173808e-03f, 5.94496654622468298501e-03f, 5.64119917254172174859e-03f, 5.33672561668945780872e-03f, 5.03167965625017643561e-03f, 4.72619457878821046942e-03f, 4.42040312709122713147e-03f, 4.11443744494245557813e-03f, 3.80842902344421868274e-03f, 3.50250864791438413365e-03f, 3.19680634537424174582e-03f, 2.89145133264915015631e-03f, 2.58657196509964968506e-03f, 2.28229568600325869593e-03f, 1.97874897660506266980e-03f, 1.67605730685465247574e-03f, 1.37434508684857771554e-03f, 1.07373561899400072825e-03f, 7.74351050912206037222e-04f, 4.76312329096932108620e-04f, 1.79739153344913828647e-04f, -1.15250068026150436743e-04f, -4.08538262157430215240e-04f, -7.00009734810518881830e-04f, -9.89550212697529359140e-04f, -1.27704688496522110984e-03f, -1.56238844381914230262e-03f, -1.84546512427596291067e-03f, -2.12616874302977649017e-03f, -2.40439273642179809562e-03f, -2.68003219750039467159e-03f, -2.95298391216083011210e-03f, -3.22314639435426720723e-03f, -3.49041992035452591087e-03f, -3.75470656207426648626e-03f, -4.01591021941965966441e-03f, -4.27393665167596914500e-03f, -4.52869350791463860101e-03f, -4.78009035641408387002e-03f, -5.02803871308742881402e-03f, -5.27245206890878791856e-03f, -5.51324591633307794364e-03f, -5.75033777470175880286e-03f, -5.98364721463032038506e-03f, -6.21309588137129026331e-03f, -6.43860751714846711591e-03f, -6.66010798245885143193e-03f, -6.87752527633716734257e-03f, -7.09078955558135361203e-03f, -7.29983315293484570641e-03f, -7.50459059422442856246e-03f, -7.70499861445137022159e-03f, -7.90099617283428028169e-03f, -8.09252446680348673513e-03f, -8.27952694494581836748e-03f, -8.46194931890021165288e-03f, -8.63973957420479179992e-03f, -8.81284798009584514900e-03f, -8.98122709826090423468e-03f, -9.14483179054685624276e-03f, -9.30361922562642808254e-03f, -9.45754888462495800494e-03f, -9.60658256571109842037e-03f, -9.75068438765514661215e-03f, -9.88982079235872779677e-03f, -1.00239605463608785763e-02f, -1.01530747413246837108e-02f, -1.02771367935108499936e-02f, -1.03961224422430293518e-02f, -1.05100097473716045521e-02f, -1.06187790857425311958e-02f, -1.07224131466778661165e-02f, -1.08208969264758890494e-02f, -1.09142177219381259629e-02f, -1.10023651229317290939e-02f, -1.10853310039956218930e-02f, -1.11631095149994884197e-02f, -1.12356970708646971419e-02f, -1.13030923403568215463e-02f, -1.13652962339602110059e-02f, -1.14223118908440956359e-02f, -1.14741446649318026840e-02f, -1.15208021100836454503e-02f, -1.15622939644049946284e-02f, -1.15986321336910645080e-02f, -1.16298306740207010868e-02f, -1.16559057735113307669e-02f, -1.16768757332475214827e-02f, -1.16927609473963332182e-02f, -1.17035838825226608945e-02f, -1.17093690561177760784e-02f, -1.17101430143551586693e-02f, -1.17059343090872795129e-02f, -1.16967734740980097013e-02f, -1.16826930006248379257e-02f, -1.16637273121658596037e-02f, -1.16399127385864407935e-02f, -1.16112874895409699111e-02f, -1.15778916272246922003e-02f, -1.15397670384720374415e-02f, -1.14969574062164479888e-02f, -1.14495081803284975280e-02f, -1.13974665478479546959e-02f, -1.13408814026266253211e-02f, -1.12798033143984600957e-02f, -1.12142844972935168402e-02f, -1.11443787778127377519e-02f, -1.10701415622809114236e-02f, -1.09916298037944538957e-02f, -1.09089019686816925125e-02f, -1.08220180024931385970e-02f, -1.07310392955389764802e-02f, -1.06360286479915983754e-02f, -1.05370502345710423397e-02f, -1.04341695688310136247e-02f, -1.03274534670632443106e-02f, -1.02169700118386209270e-02f, -1.01027885152025192345e-02f, -9.98497948154308812008e-03f, -9.86361457015006402871e-03f, -9.73876655748246930488e-03f, -9.61050929916365190286e-03f, -9.47891769172138146105e-03f, -9.34406763409175583623e-03f, -9.20603598890469380922e-03f, -9.06490054356958417647e-03f, -8.92073997117914622990e-03f, -8.77363379124968326139e-03f, -8.62366233031589164704e-03f, -8.47090668239862398803e-03f, -8.31544866936306283078e-03f, -8.15737080118616487978e-03f, -7.99675623615058242533e-03f, -7.83368874098351944402e-03f, -7.66825265095798756787e-03f, -7.50053282997436773782e-03f, -7.33061463064018075525e-03f, -7.15858385436481461928e-03f, -6.98452671148786126409e-03f, -6.80852978145714965441e-03f, -6.63067997307481386826e-03f, -6.45106448482760802543e-03f, -6.26977076531890029770e-03f, -6.08688647381931853542e-03f, -5.90249944095203298716e-03f, -5.71669762953000513278e-03f, -5.52956909556100162373e-03f, -5.34120194943696596085e-03f, -5.15168431732329797079e-03f, -4.96110430276443595266e-03f, -4.76954994852103134756e-03f, -4.57710919865432410564e-03f, -4.38386986087277181340e-03f, -4.18991956915663876782e-03f, -3.99534574667439676410e-03f, -3.80023556900675307108e-03f, -3.60467592769156538676e-03f, -3.40875339410503987864e-03f, -3.21255418369197943973e-03f, -3.01616412055992575564e-03f, -2.81966860245005685598e-03f, -2.62315256609809257030e-03f, -2.42670045299875130826e-03f, -2.23039617558575898118e-03f, -2.03432308384080993632e-03f, -1.83856393234277533909e-03f, -1.64320084776991355742e-03f, -1.44831529686655904529e-03f, -1.25398805488530435195e-03f, -1.06029917451672204415e-03f, -8.67327955316482155854e-04f, -6.75152913641518712638e-04f, -4.83851753104545291573e-04f, -2.93501335557769932588e-04f, -1.04177652615230481180e-04f, 8.40442022771478958144e-05f, 2.71090061213828637746e-04f, 4.56886708636217294885e-04f, 6.41361907564611910364e-04f, 8.24444425246958221068e-04f, 1.00606405821750295726e-03f, 1.18615165675600578790e-03f, 1.36463914874257485378e-03f, 1.54145956289825905236e-03f, 1.71654705140769636706e-03f, 1.88983691191461173828e-03f, 2.06126560888645086675e-03f, 2.23077079434063144103e-03f, 2.39829132792830895110e-03f, 2.56376729636941056573e-03f, 2.72714003223500402184e-03f, 2.88835213207216685155e-03f, 3.04734747386685260462e-03f, 3.20407123384176817371e-03f, 3.35846990258462183704e-03f, 3.51049130050470068257e-03f, 3.66008459261367522647e-03f, 3.80720030262936314294e-03f, 3.95179032639856198800e-03f, 4.09380794463911311387e-03f, 4.23320783499702736619e-03f, 4.36994608342004212803e-03f, 4.50398019484403704799e-03f, 4.63526910319382156461e-03f, 4.76377318069614620610e-03f, 4.88945424650618146178e-03f, 5.01227557464674778470e-03f, 5.13220190126144337750e-03f, 5.24919943118207308480e-03f, 5.36323584381190321402e-03f, 5.47428029832571112767e-03f, 5.58230343818897148389e-03f, 5.68727739499729628703e-03f, 5.78917579163970574818e-03f, 5.88797374478673089110e-03f, 5.98364786670789981782e-03f, 6.07617626642060343345e-03f, 6.16553855017385084303e-03f, 6.25171582127166582804e-03f, 6.33469067923863194541e-03f, 6.41444721833308011821e-03f, 6.49097102541174898749e-03f, 6.56424917715103632687e-03f, 6.63427023662958338657e-03f, 6.70102424927795491810e-03f, 6.76450273820044644529e-03f, 6.82469869887525251023e-03f, 6.88160659323871527759e-03f, 6.93522234316026366108e-03f, 6.98554332331408935064e-03f, 7.03256835345506155222e-03f, 7.07629769010476809138e-03f, 7.11673301765615093362e-03f, 7.15387743890304877992e-03f, 7.18773546500291789924e-03f, 7.21831300488032408247e-03f, 7.24561735407938580650e-03f, 7.26965718307318129604e-03f, 7.29044252503875406940e-03f, 7.30798476310635155423e-03f, 7.32229661709144288850e-03f, 7.33339212971884264747e-03f, 7.34128665234775375920e-03f, 7.34599683020745793799e-03f, 7.34754058715258225737e-03f, 7.34593710994830336597e-03f, 7.34120683209452638829e-03f, 7.33337141719967496728e-03f, 7.32245374191355016119e-03f, 7.30847787843014878861e-03f, 7.29146907657012011139e-03f, 7.27145374545387114529e-03f, 7.24845943477565521351e-03f, 7.22251481568945107037e-03f, 7.19364966131744686118e-03f, 7.16189482689201083881e-03f, 7.12728222954231872138e-03f, 7.08984482773655864257e-03f, 7.04961660039112210374e-03f, 7.00663252565801673161e-03f, 6.96092855940177307472e-03f, 6.91254161337735619636e-03f, 6.86150953312070904788e-03f, 6.80787107556324582597e-03f, 6.75166588638215301593e-03f, 6.69293447709806265528e-03f, 6.63171820193170571955e-03f, 6.56805923443159328512e-03f, 6.50200054388410785683e-03f, 6.43358587151825807998e-03f, 6.36285970651646794888e-03f, 6.28986726184373092646e-03f, 6.21465444990643503531e-03f, 6.13726785805332464285e-03f, 6.05775472392990760317e-03f, 5.97616291069856791357e-03f, 5.89254088213594148099e-03f, 5.80693767761965816410e-03f, 5.71940288701587758180e-03f, 5.62998662548002196115e-03f, 5.53873950818146131014e-03f, 5.44571262496510149348e-03f, 5.35095751496040238082e-03f, 5.25452614115022934027e-03f, 5.15647086491062122543e-03f, 5.05684442053339500839e-03f, 4.95569988974256699088e-03f, 4.85309067621648645985e-03f, 4.74907048012647350216e-03f, 4.64369327270371719946e-03f, 4.53701327084515566163e-03f, 4.42908491176951992635e-03f, 4.31996282773485212186e-03f, 4.20970182082771107734e-03f, 4.09835683783572966160e-03f, 3.98598294521311340144e-03f, 3.87263530415101094040e-03f, 3.75836914576165720403e-03f, 3.64323974638825019007e-03f, 3.52730240304928790995e-03f, 3.41061240902878646739e-03f, 3.29322502962129748730e-03f, 3.17519547804233142826e-03f, 3.05657889151338601694e-03f, 2.93743030753160130203e-03f, 2.81780464033296821486e-03f, 2.69775665755896121301e-03f, 2.57734095713514719389e-03f, 2.45661194437134461702e-03f, 2.33562380929147129019e-03f, 2.21443050420279223534e-03f, 2.09308572151161147862e-03f, 1.97164287179554201940e-03f, 1.85015506213867531038e-03f, 1.72867507473943343883e-03f, 1.60725534579748128607e-03f, 1.48594794468843234732e-03f, 1.36480455343317803527e-03f, 1.24387644646943291808e-03f, 1.12321447073277739387e-03f, 1.00286902605367005473e-03f, 8.82890045877847201225e-04f, 7.63326978315998568199e-04f, 6.44228767529792380013e-04f, 5.25643835459782418976e-04f, 4.07620063901896608968e-04f, 2.90204776937506045247e-04f, 1.73444723723805766706e-04f, 5.73860616484244659592e-05f, -5.79256601447129809831e-05f, -1.72445516855139978872e-04f, -2.86129223744603178401e-04f, -3.98933151471683798521e-04f, -5.10814341036775051638e-04f, -6.21730518333987194034e-04f, -7.31640108305292775383e-04f, -8.40502248693749103720e-04f, -9.48276803391555388537e-04f, -1.05492437538016840815e-03f, -1.16040631925863960139e-03f, -1.26468475335793167046e-03f, -1.36772257143744795961e-03f, -1.46948345396231690001e-03f, -1.56993187895820992227e-03f, -1.66903313244173671925e-03f, -1.76675331842487395514e-03f, -1.86305936849075486246e-03f, -1.95791905094055886799e-03f, -2.05130097950870097026e-03f, -2.14317462164644121497e-03f, -2.23351030637205421117e-03f, -2.32227923168745655630e-03f, -2.40945347156019864382e-03f, -2.49500598247101752769e-03f, -2.57891060952624437755e-03f, -2.66114209213571661222e-03f, -2.74167606925580006200e-03f, -2.82048908419860059130e-03f, -2.89755858900738426376e-03f, -2.97286294839969704451e-03f, -3.04638144327833843006e-03f, -3.11809427381250833106e-03f, -3.18798256208930414976e-03f, -3.25602835433819615824e-03f, -3.32221462272949338845e-03f, -3.38652526674922864716e-03f, -3.44894511415224942069e-03f, -3.50945992149601917673e-03f, -3.56805637425754813494e-03f, -3.62472208653601482564e-03f, -3.67944560034401835918e-03f, -3.73221638449032109761e-03f, -3.78302483305743104600e-03f, -3.83186226347697993233e-03f, -3.87872091420681094562e-03f, -3.92359394201303914723e-03f, -3.96647541886111301701e-03f, -4.00736032841956212047e-03f, -4.04624456218095809173e-03f, -4.08312491520368761599e-03f, -4.11799908147960237043e-03f, -4.15086564893147550587e-03f, -4.18172409404541419592e-03f, -4.21057477614258761356e-03f, -4.23741893129556731340e-03f, -4.26225866589410255086e-03f, -4.28509694986558761776e-03f, -4.30593760955555893838e-03f, -4.32478532027361865092e-03f, -4.34164559851025946141e-03f, -4.35652479383043678834e-03f, -4.36943008044940375129e-03f, -4.38036944849687272935e-03f, -4.38935169497548655082e-03f, -4.39638641441941844384e-03f, -4.40148398925969356471e-03f, -4.40465557990201740657e-03f, -4.40591311452397972614e-03f, -4.40526927859759102890e-03f, -4.40273750414399336200e-03f, -4.39833195872687317957e-03f, -4.39206753419116562726e-03f, -4.38395983515392842489e-03f, -4.37402516725410826781e-03f, -4.36228052516814471251e-03f, -4.34874358039834783829e-03f, -4.33343266884099378305e-03f, -4.31636677814128347924e-03f, -4.29756553484226828249e-03f, -4.27704919133478460302e-03f, -4.25483861261575103258e-03f, -4.23095526286197242544e-03f, -4.20542119182673330285e-03f, -4.17825902106652539991e-03f, -4.14949193000528453179e-03f, -4.11914364184333848390e-03f, -4.08723840931864319803e-03f, -4.05380100032764340012e-03f, -4.01885668341308427420e-03f, -3.98243121312639048598e-03f, -3.94455081527187324114e-03f, -3.90524217204034265055e-03f, -3.86453240703949710971e-03f, -3.82244907022857112119e-03f, -3.77902012276458334344e-03f, -3.73427392176791782957e-03f, -3.68823920501411533363e-03f, -3.64094507555994461798e-03f, -3.59242098631046497328e-03f, -3.54269672453505143905e-03f, -3.49180239633924225512e-03f, -3.43976841109999795926e-03f, -3.38662546587152699790e-03f, -3.33240452976901182711e-03f, -3.27713682833726460339e-03f, -3.22085382791172753977e-03f, -3.16358721997866921757e-03f, -3.10536890554190866259e-03f, -3.04623097950279270868e-03f, -2.98620571506079835605e-03f, -2.92532554814109750294e-03f, -2.86362306185669341502e-03f, -2.80113097101106212072e-03f, -2.73788210664884781517e-03f, -2.67390940066071719841e-03f, -2.60924587044905360173e-03f, -2.54392460366138209102e-03f, -2.47797874299740972931e-03f, -2.41144147109654920225e-03f, -2.34434599551184728525e-03f, -2.27672553377682423265e-03f, -2.20861329857108918892e-03f, -2.14004248299099744321e-03f, -2.07104624593109319652e-03f, -2.00165769758243626206e-03f, -1.93190988505322330110e-03f, -1.86183577811795423693e-03f, -1.79146825509988766485e-03f, -1.72084008889326943795e-03f, -1.64998393312966343087e-03f, -1.57893230849462818527e-03f, -1.50771758919912264932e-03f, -1.43637198961127387532e-03f, -1.36492755105304883201e-03f, -1.29341612876705033125e-03f, -1.22186937905778840625e-03f, -1.15031874661246304344e-03f, -1.07879545200534983015e-03f, -1.00733047939059738862e-03f, -9.35954564387392199190e-04f, -8.64698182161781326270e-04f, -7.93591535709335898878e-04f, -7.22664544342259342231e-04f, -6.51946832385152269008e-04f, -5.81467718082645003419e-04f, -5.11256202723101406751e-04f, -4.41340959980976682391e-04f, -3.71750325482115011495e-04f, -3.02512286594312090893e-04f, -2.33654472446860898751e-04f, -1.65204144181543805632e-04f, -9.71881854382950145007e-05f, -2.96330930778333087529e-05f, 3.74350318557888840534e-05f, 1.03990492930665522531e-04f, 1.70008006877860913661e-04f, 2.35462711895771908792e-04f, 3.00330175705594570618e-04f, 3.64586403356421419591e-04f, 4.28207844777551787079e-04f, 4.91171402077186748311e-04f, 5.53454436585029511952e-04f, 6.15034775638214118408e-04f, 6.75890719108685204992e-04f, 7.36001045671158494674e-04f, 7.95345018810736208159e-04f, 8.53902392568785988461e-04f, 9.11653417026953884206e-04f, 9.68578843528107927274e-04f, 1.02465992963409239201e-03f, 1.07987844381954517861e-03f, 1.13421666990184857697e-03f, 1.18765741120669863183e-03f, 1.24018399446962118877e-03f, 1.29178027347317181048e-03f, 1.34243063242041153760e-03f, 1.39211998904448393775e-03f, 1.44083379745542361494e-03f, 1.48855805072399061464e-03f, 1.53527928320389963832e-03f, 1.58098457259274222282e-03f, 1.62566154173287799080e-03f, 1.66929836015300469068e-03f, 1.71188374535185877309e-03f, 1.75340696382495094506e-03f, 1.79385783183615881223e-03f, 1.83322671593499951850e-03f, 1.87150453322187351919e-03f, 1.90868275136241117781e-03f, 1.94475338835288632730e-03f, 1.97970901203875114180e-03f, 2.01354273938795765367e-03f, 2.04624823552140497340e-03f, 2.07781971250251560127e-03f, 2.10825192788842950831e-03f, 2.13754018304492627786e-03f, 2.16568032122775241954e-03f, 2.19266872543273649843e-03f, 2.21850231601753312277e-03f, 2.24317854809751244041e-03f, 2.26669540871874157159e-03f, 2.28905141381085521987e-03f, 2.31024560492300908704e-03f, 2.33027754574556848419e-03f, 2.34914731842113299123e-03f, 2.36685551964774167771e-03f, 2.38340325657774047483e-03f, 2.39879214251553785076e-03f, 2.41302429241780800814e-03f, 2.42610231819949092105e-03f, 2.43802932384921434983e-03f, 2.44880890035772524199e-03f, 2.45844512046309356806e-03f, 2.46694253321619750424e-03f, 2.47430615837057783779e-03f, 2.48054148060029584083e-03f, 2.48565444354972896537e-03f, 2.48965144371932805070e-03f, 2.49253932419119233338e-03f, 2.49432536819870756192e-03f, 2.49501729254406187306e-03f, 2.49462324086802481049e-03f, 2.49315177677595679884e-03f, 2.49061187682437026880e-03f, 2.48701292337218105022e-03f, 2.48236469730101589823e-03f, 2.47667737060877201499e-03f, 2.46996149888082565729e-03f, 2.46222801364318543901e-03f, 2.45348821460201170497e-03f, 2.44375376177385136151e-03f, 2.43303666751100867299e-03f, 2.42134928842652587602e-03f, 2.40870431722312039469e-03f, 2.39511477443066006163e-03f, 2.38059400005651662299e-03f, 2.36515564515335941637e-03f, 2.34881366330883401689e-03f, 2.33158230206161314751e-03f, 2.31347609424828503169e-03f, 2.29450984928561332182e-03f, 2.27469864439260342423e-03f, 2.25405781575686164908e-03f, 2.23260294964972971082e-03f, 2.21034987349466626252e-03f, 2.18731464689328946802e-03f, 2.16351355261355186160e-03f, 2.13896308754443764677e-03f, 2.11367995362164717843e-03f, 2.08768104872860673846e-03f, 2.06098345757712827012e-03f, 2.03360444257223585765e-03f, 2.00556143466523338278e-03f, 1.97687202419953838434e-03f, 1.94755395175337346625e-03f, 1.91762509898370150235e-03f, 1.88710347947544372464e-03f, 1.85600722960030926380e-03f, 1.82435459938927956557e-03f, 1.79216394342290039413e-03f, 1.75945371174334418141e-03f, 1.72624244079242210488e-03f, 1.69254874437936271916e-03f, 1.65839130468247841071e-03f, 1.62378886328829906069e-03f, 1.58876021227249563002e-03f, 1.55332418532590395277e-03f, 1.51749964892962602506e-03f, 1.48130549358308134279e-03f, 1.44476062508820820261e-03f, 1.40788395589385340219e-03f, 1.37069439650360901800e-03f, 1.33321084695073701146e-03f, 1.29545218834344489506e-03f, 1.25743727448406820806e-03f, 1.21918492356532437090e-03f, 1.18071390994699786103e-03f, 1.14204295601606406994e-03f, 1.10319072413361980722e-03f, 1.06417580867146756643e-03f, 1.02501672814153568618e-03f, 9.85731917420752321024e-04f, 9.46339720074761028661e-04f, 9.06858380782653099826e-04f, 8.67306037865940180828e-04f, 8.27700715924154601863e-04f, 7.88060318579825755218e-04f, 7.48402621335140681366e-04f, 7.08745264542995573667e-04f, 6.69105746494616255432e-04f, 6.29501416626196083105e-04f, 5.89949468846577432593e-04f, 5.50466934988384673337e-04f, 5.11070678384569518186e-04f, 4.71777387572266644969e-04f, 4.32603570126133903009e-04f, 3.93565546622840108093e-04f, 3.54679444738640930936e-04f, 3.15961193481461178213e-04f, 2.77426517559644606348e-04f, 2.39090931888283949464e-04f, 2.00969736235134577339e-04f, 1.63078010007227617185e-04f, 1.25430607179671989044e-04f, 8.80421513676910678318e-05f, 5.09270310433270975443e-05f, 1.40993948977051206995e-05f, -2.24268526499689963992e-05f, -5.86380557959355767418e-05f, -9.45208115468090933828e-05f, -1.30061973765318488264e-04f, -1.65248657065980185041e-04f, -2.00068240560348736649e-04f, -2.34508371450769772170e-04f, -2.68556968472767403858e-04f, -3.02202225185153135114e-04f, -3.35432613107714699702e-04f, -3.68236884706315019053e-04f, -4.00604076224976460123e-04f, -4.32523510365022737217e-04f, -4.63984798811095579957e-04f, -4.94977844604242624843e-04f, -5.25492844361963827582e-04f, -5.55520290345540247376e-04f, -5.85050972374773735341e-04f, -6.14075979590610184985e-04f, -6.42586702065795259929e-04f, -6.70574832264203037069e-04f, -6.98032366349193725621e-04f, -7.24951605341828033552e-04f, -7.51325156129156322866e-04f, -7.77145932323820387393e-04f, -8.02407154975299293648e-04f, -8.27102353133903219450e-04f, -8.51225364268258727699e-04f, -8.74770334537431802288e-04f, -8.97731718918501797773e-04f, -9.20104281190827605862e-04f, -9.41883093778050233848e-04f, -9.63063537449165905750e-04f, -9.83641300879760935652e-04f, -1.00361238007481306557e-03f, -1.02297307765449128208e-03f, -1.04172000200423238610e-03f, -1.05985006629069969386e-03f, -1.07736048734502422482e-03f, -1.09424878441514257174e-03f, -1.11051277778848088776e-03f, -1.12615058728708443934e-03f, -1.14116063063659394818e-03f, -1.15554162171100749217e-03f, -1.16929256865495575027e-03f, -1.18241277188544584124e-03f, -1.19490182197486941203e-03f, -1.20675959741726535404e-03f, -1.21798626227982297217e-03f, -1.22858226374166065384e-03f, -1.23854832952187280184e-03f, -1.24788546519901061629e-03f, -1.25659495142406364872e-03f, -1.26467834102919943727e-03f, -1.27213745603428412742e-03f, -1.27897438455362244883e-03f, -1.28519147760499177768e-03f, -1.29079134582336268501e-03f, -1.29577685608154116216e-03f, -1.30015112802015094483e-03f, -1.30391753048923449490e-03f, -1.30707967790388737521e-03f, -1.30964142651636395419e-03f, -1.31160687060702277167e-03f, -1.31298033859659005196e-03f, -1.31376638908218385492e-03f, -1.31396980679961724973e-03f, -1.31359559851441281067e-03f, -1.31264898884408520024e-03f, -1.31113541601419526274e-03f, -1.30906052755070225271e-03f, -1.30643017591116055581e-03f, -1.30325041405731484843e-03f, -1.29952749097163636725e-03f, -1.29526784712039586857e-03f, -1.29047810986578645812e-03f, -1.28516508882973415524e-03f, -1.27933577121191956356e-03f, -1.27299731706460124078e-03f, -1.26615705452684046764e-03f, -1.25882247502064623448e-03f, -1.25100122841167137044e-03f, -1.24270111813698665680e-03f, -1.23393009630251375940e-03f, -1.22469625875265661526e-03f, -1.21500784011470538623e-03f, -1.20487320882053006339e-03f, -1.19430086210808427544e-03f, -1.18329942100529298017e-03f, -1.17187762529875026674e-03f, -1.16004432848981092231e-03f, -1.14780849274044756403e-03f, -1.13517918381147498753e-03f, -1.12216556599544479457e-03f, -1.10877689704675665670e-03f, -1.09502252311132830878e-03f, -1.08091187365826806380e-03f, -1.06645445641585442931e-03f, -1.05165985231426076241e-03f, -1.03653771043727446592e-03f, -1.02109774298538512859e-03f, -1.00534972025243104707e-03f, -9.89303465618205854493e-04f, -9.72968850559070386023e-04f, -9.56355789678956110487e-04f, -9.39474235762757228438e-04f, -9.22334174854466432199e-04f, -9.04945621361946477522e-04f, -8.87318613190598128385e-04f, -8.69463206907894578697e-04f, -8.51389472940857513876e-04f, -8.33107490808391681729e-04f, -8.14627344390523211622e-04f, -7.95959117236444131487e-04f, -7.77112887913182157074e-04f, -7.58098725396869001399e-04f, -7.38926684508335205812e-04f, -7.19606801394884719458e-04f, -7.00149089059903611230e-04f, -6.80563532942129469604e-04f, -6.60860086546161931871e-04f, -6.41048667125920334890e-04f, -6.21139151422514545521e-04f, -6.01141371458319205383e-04f, -5.81065110388481308873e-04f, -5.60920098411564179222e-04f, -5.40716008740645473485e-04f, -5.20462453636329636791e-04f, -5.00168980502928035153e-04f, -4.79845068049252442000e-04f, -4.59500122515179752151e-04f, -4.39143473965308969373e-04f, -4.18784372650737749064e-04f, -3.98431985440370181519e-04f, -3.78095392322528038760e-04f, -3.57783582978218623466e-04f, -3.37505453426840712917e-04f, -3.17269802745462131330e-04f, -2.97085329862539673351e-04f, -2.76960630426857884294e-04f, -2.56904193752789059696e-04f, -2.36924399842341755311e-04f, -2.17029516485020210204e-04f, -1.97227696436049984184e-04f, -1.77526974673721080374e-04f, -1.57935265736364822877e-04f, -1.38460361139671443579e-04f, -1.19109926874780676581e-04f, -9.98915009877096604120e-05f, -8.08124912404658787314e-05f, -6.18801728543761411219e-05f, -4.31016863358901726119e-05f, -2.44840353852607290751e-05f, -6.03408488822807525549e-06f, 1.22414410087942923482e-05f, 3.03359607401630166279e-05f, 4.82430370786643698930e-05f, 6.59563788094303331657e-05f, 8.34698423199615796228e-05f, 1.00777433106333784039e-04f, 1.17873307195481496850e-04f, 1.34751772483674884346e-04f, 1.51407289991337552218e-04f, 1.67834475034225882872e-04f, 1.84028098311239825778e-04f, 1.99983086909011169297e-04f, 2.15694525223608961004e-04f, 2.31157655799539194473e-04f, 2.46367880086458945071e-04f, 2.61320759113890506518e-04f, 2.76012014084471178285e-04f, 2.90437526885940853344e-04f, 3.04593340522622661477e-04f, 3.18475659466672001308e-04f, 3.32080849929764377465e-04f, 3.45405440055728798956e-04f, 3.58446120034815369788e-04f, 3.71199742140139986053e-04f, 3.83663320687053425832e-04f, 3.95834031916090828447e-04f, 4.07709213800306149479e-04f, 4.19286365777668133727e-04f, 4.30563148409382728376e-04f, 4.41537382964913725693e-04f, 4.52207050934656925797e-04f, 4.62570293470961546242e-04f, 4.72625410758635230730e-04f, 4.82370861315684551149e-04f, 4.91805261225328150740e-04f, 5.00927383300294468485e-04f, 5.09736156180340470138e-04f, 5.18230663364083014855e-04f, 5.26410142176160977465e-04f, 5.34273982670858136115e-04f, 5.41821726473215733132e-04f, 5.49053065558819199977e-04f, 5.55967840973343140065e-04f, 5.62566041493084596781e-04f, 5.68847802227587348751e-04f, 5.74813403165606124842e-04f, 5.80463267665585255090e-04f, 5.85797960891953992807e-04f, 5.90818188198352634011e-04f, 5.95524793459205619046e-04f, 5.99918757350793364382e-04f, 6.04001195583171897330e-04f, 6.07773357084216074402e-04f, 6.11236622137123529822e-04f, 6.14392500472671181100e-04f, 6.17242629317575641247e-04f, 6.19788771400296852575e-04f, 6.22032812915646266330e-04f, 6.23976761449542352556e-04f, 6.25622743865282679096e-04f, 6.26973004152734866845e-04f, 6.28029901241785953013e-04f, 6.28795906781460817864e-04f, 6.29273602886090081127e-04f, 6.29465679849935955126e-04f, 6.29374933831637162034e-04f, 6.29004264509917614644e-04f, 6.28356672711923979618e-04f, 6.27435258015602670238e-04f, 6.26243216327530610385e-04f, 6.24783837437580067548e-04f, 6.23060502551831571320e-04f, 6.21076681805128117189e-04f, 6.18835931754663313292e-04f, 6.16341892855999597296e-04f, 6.13598286922899423257e-04f, 6.10608914572360016171e-04f, 6.07377652656219848941e-04f, 6.03908451680720608987e-04f, 6.00205333215381750360e-04f, 5.96272387292547546150e-04f, 5.92113769798962869678e-04f, 5.87733699860716260477e-04f, 5.83136457222883410056e-04f, 5.78326379625196070923e-04f, 5.73307860175058681823e-04f, 5.68085344719199331932e-04f, 5.62663329215264671791e-04f, 5.57046357104629347515e-04f, 5.51239016687691835844e-04f, 5.45245938502918582949e-04f, 5.39071792710859535452e-04f, 5.32721286484389919269e-04f, 5.26199161406370888652e-04f, 5.19510190875949965511e-04f, 5.12659177524647379116e-04f, 5.05650950643461694588e-04f, 4.98490363622070692395e-04f, 4.91182291401325650700e-04f, 4.83731627940122419548e-04f, 4.76143283697776802978e-04f, 4.68422183132952726740e-04f, 4.60573262220249240485e-04f, 4.52601465985464203410e-04f, 4.44511746060588389465e-04f, 4.36309058259498746754e-04f, 4.27998360175392566791e-04f, 4.19584608800889610671e-04f, 4.11072758171783069880e-04f, 4.02467757035333936928e-04f, 3.93774546544049656484e-04f, 3.84998057975832470683e-04f, 3.76143210481313700240e-04f, 3.67214908859312392543e-04f, 3.58218041361136620669e-04f, 3.49157477524604336044e-04f, 3.40038066038525694287e-04f, 3.30864632638427479810e-04f, 3.21641978034209344382e-04f, 3.12374875870502510550e-04f, 3.03068070720368721877e-04f, 2.93726276113044930307e-04f, 2.84354172596324122988e-04f, 2.74956405834251703824e-04f, 2.65537584740686090270e-04f, 2.56102279649332684359e-04f, 2.46655020520732712157e-04f, 2.37200295186851598233e-04f, 2.27742547633623577692e-04f, 2.18286176322046336163e-04f, 2.08835532548216017655e-04f, 1.99394918842769620351e-04f, 1.89968587410093434423e-04f, 1.80560738607737464008e-04f, 1.71175519466368710516e-04f, 1.61817022250571095152e-04f, 1.52489283060860649410e-04f, 1.43196280477158617963e-04f, 1.33941934244020521144e-04f, 1.24730103997818122337e-04f, 1.15564588036148766046e-04f, 1.06449122129624024050e-04f, 9.73873783762444747338e-05f, 8.83829640984437603628e-05f, 7.94394207830501368397e-05f, 7.05602230641164422306e-05f, 6.17487777488439845697e-05f, 5.30084228865277819307e-05f, 4.43424268807018922721e-05f, 3.57539876443504868529e-05f, 2.72462317982963840894e-05f, 1.88222139126731892130e-05f, 1.04849157914673770899e-05f, 2.23724580001398245952e-06f, -5.91796176458841909860e-06f, -1.39779472603022615363e-05f, -2.19400262443108254274e-05f, -2.98015899747312779436e-05f, -3.75601058699388188874e-05f, -4.52131179258346812058e-05f, -5.27582470911579511194e-05f, -6.01931916011169823111e-05f, -6.75157272696155086192e-05f, -7.47237077402686759994e-05f, -8.18150646965978259242e-05f, -8.87878080315813176041e-05f, -9.56400259770174112402e-05f, -1.02369885192932295447e-04f, -1.08975630817447491838e-04f, -1.15455586477445233109e-04f, -1.21808154260462635915e-04f, -1.28031814648165977134e-04f, -1.34125126411867404445e-04f, -1.40086726470483718130e-04f, -1.45915329711446663599e-04f, -1.51609728774925367391e-04f, -1.57168793801964925671e-04f, -1.62591472146915872604e-04f, -1.67876788054762332352e-04f, -1.73023842303760092562e-04f, -1.78031811814023872061e-04f, -1.82899949222519527144e-04f, -1.87627582425063416539e-04f, -1.92214114085866635134e-04f, -1.96659021115234305434e-04f, -2.00961854115964548605e-04f, -2.05122236799067813760e-04f, -2.09139865369424642018e-04f, -2.13014507881963919287e-04f, -2.16746003569016719827e-04f, -2.20334262139455170628e-04f, -2.23779263050288623272e-04f, -2.27081054751326671731e-04f, -2.30239753903598189230e-04f, -2.33255544572162539776e-04f, -2.36128677394024743099e-04f, -2.38859468721759089912e-04f, -2.41448299743606627134e-04f, -2.43895615580664562507e-04f, -2.46201924361897988517e-04f, -2.48367796277648787261e-04f, -2.50393862612362249915e-04f, -2.52280814757210175570e-04f, -2.54029403203337271477e-04f, -2.55640436516424906718e-04f, -2.57114780293308988878e-04f, -2.58453356101321892260e-04f, -2.59657140401130335366e-04f, -2.60727163453730896234e-04f, -2.61664508212373382850e-04f, -2.62470309200077975623e-04f, -2.63145751373508769710e-04f, -2.63692068973905575490e-04f, -2.64110544365773612825e-04f, -2.64402506864084673854e-04f, -2.64569331550674807438e-04f, -2.64612438080565591254e-04f, -2.64533289478916812908e-04f, -2.64333390929326621406e-04f, -2.64014288554176887285e-04f, -2.63577568187730454374e-04f, -2.63024854142685936189e-04f, -2.62357807970878796362e-04f, -2.61578127218826830911e-04f, -2.60687544178804346540e-04f, -2.59687824636131118095e-04f, -2.58580766613355161020e-04f, -2.57368199111998000473e-04f, -2.56051980852535184582e-04f, -2.54633999013272917245e-04f, -2.53116167968770410229e-04f, -2.51500428028467203686e-04f, -2.49788744176148520739e-04f, -2.47983104810888498049e-04f, -2.46085520490098991209e-04f, -2.44098022675305284133e-04f, -2.42022662481256641692e-04f, -2.39861509428983846149e-04f, -2.37616650203396260988e-04f, -2.35290187416002671578e-04f, -2.32884238373345356290e-04f, -2.30400933851706944807e-04f, -2.27842416878660760553e-04f, -2.25210841522004905167e-04f, -2.22508371686640725982e-04f, -2.19737179919906623887e-04f, -2.16899446225907947382e-04f, -2.13997356889345798732e-04f, -2.11033103309352538255e-04f, -2.08008880843819519632e-04f, -2.04926887664708660573e-04f, -2.01789323624807442155e-04f, -1.98598389136399967663e-04f, -1.95356284062287273917e-04f, -1.92065206619610498147e-04f, -1.88727352296894345393e-04f, -1.85344912784731391314e-04f, -1.81920074920499755819e-04f, -1.78455019647530454055e-04f, -1.74951920989073971512e-04f, -1.71412945037461607323e-04f, -1.67840248958807175192e-04f, -1.64235980013589989322e-04f, -1.60602274593463965847e-04f, -1.56941257274607583660e-04f, -1.53255039887926981963e-04f, -1.49545720606400077326e-04f, -1.45815383049865629223e-04f, -1.42066095407511174204e-04f, -1.38299909578337953649e-04f, -1.34518860329830815736e-04f, -1.30724964475093245915e-04f, -1.26920220068654472992e-04f, -1.23106605621173758290e-04f, -1.19286079333219134950e-04f, -1.15460578348350841244e-04f, -1.11632018025632952099e-04f, -1.07802291231779830637e-04f, -1.03973267653065606659e-04f, -1.00146793127148565233e-04f, -9.63246889949230804435e-05f, -9.25087514725346873358e-05f, -8.87007510436452450671e-05f, -8.49024318720545515026e-05f, -8.11155112347393549661e-05f, -7.73416789754019609908e-05f, -7.35825969785674924512e-05f, -6.98398986642887023612e-05f, -6.61151885034750767074e-05f, -6.24100415538899748772e-05f, -5.87260030168200428450e-05f, -5.50645878143974986741e-05f, -5.14272801876128782695e-05f, -4.78155333149380714058e-05f, -4.42307689515766361233e-05f, -4.06743770892715146635e-05f, -3.71477156366324749162e-05f, -3.36521101199027279356e-05f, -3.01888534041130525139e-05f, -2.67592054345224943665e-05f, -2.33643929982665948157e-05f, -2.00056095060906951114e-05f, -1.66840147940797201324e-05f, -1.34007349452439661363e-05f, -1.01568621308437851693e-05f, -6.95345447128813701427e-06f, -3.79153591650250810366e-06f, -6.72096145553293655593e-07f, 2.40390951459279597616e-06f, 5.43556015137456058426e-06f, 8.42196931017640123499e-06f, 1.13622850193110927676e-05f, 1.42556897966238257746e-05f, 1.71014006379341248229e-05f, 1.98986689875390831126e-05f, 2.26467806909793172563e-05f, 2.53450559303131682314e-05f, 2.79928491421220740245e-05f, 3.05895489185053302676e-05f, 3.31345778912925430443e-05f, 3.56273925997451632237e-05f, 3.80674833419947510364e-05f, 4.04543740105068599521e-05f, 4.27876219118044463113e-05f, 4.50668175707753267513e-05f, 4.72915845198089502860e-05f, 4.94615790730713200283e-05f, 5.15764900862005805371e-05f, 5.36360387017334339240e-05f, 5.56399780805481871900e-05f, 5.75880931196438774090e-05f, 5.94802001565561985590e-05f, 6.13161466607366493955e-05f, 6.30958109121965409621e-05f, 6.48191016677504891876e-05f, 6.64859578151715343739e-05f, 6.80963480156043240521e-05f, 6.96502703345347587470e-05f, 7.11477518616810702089e-05f, 7.25888483201263885920e-05f, 7.39736436650163245096e-05f, 7.53022496721903708853e-05f, 7.65748055170595645635e-05f, 7.77914773440878826866e-05f, 7.89524578272120806021e-05f, 8.00579657215541123884e-05f, 8.11082454067584148567e-05f, 8.21035664223097817662e-05f, 8.30442229951717527346e-05f, 8.39305335600990616227e-05f, 8.47628402729624154692e-05f, 8.55415085174379832356e-05f, 8.62669264054029689666e-05f, 8.69395042713942022743e-05f, 8.75596741614536786501e-05f, 8.81278893167311284636e-05f, 8.86446236521661512422e-05f, 8.91103712306026604075e-05f, 8.95256457326717546962e-05f, 8.98909799227875892661e-05f, 9.02069251115862640782e-05f, 9.04740506151490363976e-05f, 9.06929432113401867103e-05f, 9.08642065935940279782e-05f, 9.09884608224771455898e-05f, 9.10663417753510066833e-05f, 9.10985005944656069544e-05f, 9.10856031337957275460e-05f, 9.10283294049456589850e-05f, 9.09273730224310103125e-05f, 9.07834406486567112131e-05f, 9.05972514388884682737e-05f, 9.03695364865346147717e-05f, 9.01010382690298824038e-05f, 8.97925100946232214449e-05f, 8.94447155503604388049e-05f, 8.90584279515538464710e-05f, 8.86344297930203114553e-05f, 8.81735122023717546660e-05f, 8.76764743956322256668e-05f, 8.71441231354561004216e-05f, 8.65772721922116491981e-05f, 8.59767418081955635050e-05f, 8.53433581652348287718e-05f, 8.46779528559294970011e-05f, 8.39813623587842351167e-05f, 8.32544275174723505482e-05f, 8.24979930244696123580e-05f, 8.17129069092911340059e-05f, 8.09000200315580515319e-05f, 8.00601855791177764851e-05f, 7.91942585714318722121e-05f, 7.83030953684469709260e-05f, 7.73875531851490379041e-05f, 7.64484896120066966278e-05f, 7.54867621414943666810e-05f, 7.45032277008856680736e-05f, 7.34987421914997258445e-05f, 7.24741600345781298914e-05f, 7.14303337239639759421e-05f, 7.03681133857495075701e-05f, 6.92883463450485250238e-05f, 6.81918767000556897513e-05f, 6.70795449035298084342e-05f, 6.59521873518545653477e-05f, 6.48106359818019012913e-05f, 6.36557178751403541801e-05f, 6.24882548712000070136e-05f, 6.13090631875229413088e-05f, 6.01189530487050480105e-05f, 5.89187283235398522627e-05f, 5.77091861705600261078e-05f, 5.64911166920768371556e-05f, 5.52653025968012133018e-05f, 5.40325188711331935503e-05f, 5.27935324591918643858e-05f, 5.15491019516612150597e-05f, 5.02999772835162253098e-05f, 4.90468994406823114189e-05f, 4.77906001756943685883e-05f, 4.65318017323872241855e-05f, 4.52712165796745444508e-05f, 4.40095471544385224527e-05f, 4.27474856135762158195e-05f, 4.14857135952114382585e-05f, 4.02249019891050019768e-05f, 3.89657107162695045101e-05f, 3.77087885178019617646e-05f, 3.64547727529321324196e-05f, 3.52042892062907349744e-05f, 3.39579519043854685862e-05f, 3.27163629412759540162e-05f, 3.14801123134230239524e-05f, 3.02497777637023744978e-05f, 2.90259246345387218443e-05f, 2.78091057301432964695e-05f, 2.65998611878025348972e-05f, 2.53987183581919854364e-05f, 2.42061916946511448772e-05f, 2.30227826513813061706e-05f, 2.18489795905047869758e-05f, 2.06852576979222047276e-05f, 1.95320789079097866306e-05f, 1.83898918363816234168e-05f, 1.72591317227478167385e-05f, 1.61402203802856600485e-05f, 1.50335661549492530784e-05f, 1.39395638925266073790e-05f, 1.28585949140593193966e-05f, 1.17910269994264146578e-05f, 1.07372143790027249535e-05f, 9.69749773328629209753e-06f, 8.67220420039572420566e-06f, 7.66164739132314657512e-06f, 6.66612741284622437347e-06f, 5.68593089796976951548e-06f, 4.72133104379410701050e-06f, 3.77258765668374723169e-06f, 2.83994720461857994069e-06f, 1.92364287659795633360e-06f, 1.02389464897677183691e-06f, 1.40909358598783243636e-07f, -7.25119217400925882488e-07f, -1.57401027526573751796e-06f, -2.40559589188401177682e-06f, -3.21972092580435969379e-06f, -4.01624291213239929447e-06f, -4.79503195145223449727e-06f, -5.55597059291959898474e-06f, -6.29895371166915398527e-06f, -7.02388838068995278969e-06f, -7.73069373730662337714e-06f, -8.41930084443043260571e-06f, -9.08965254672049638874e-06f, -9.74170332181420186798e-06f, -1.03754191267770145960e-05f, -1.09907772399306088372e-05f, -1.15877660982095854786e-05f, -1.21663851302071818753e-05f, -1.27266445850638015879e-05f, -1.32685653573596831328e-05f, -1.37921788081646564831e-05f, -1.42975265824070264185e-05f, -1.47846604227169150120e-05f, -1.52536419799077777845e-05f, -1.57045426202454864190e-05f, -1.61374432296745794440e-05f, -1.65524340151509907705e-05f, -1.69496143032443776877e-05f, -1.73290923361651281922e-05f, -1.76909850653765942752e-05f, -1.80354179429455134585e-05f, -1.83625247107888849940e-05f, -1.86724471879742999741e-05f, -1.89653350562250020523e-05f, -1.92413456437866339777e-05f, -1.95006437078064384194e-05f, -1.97434012153796016272e-05f, -1.99697971234102591856e-05f, -2.01800171574394671614e-05f, -2.03742535895865872536e-05f, -2.05527050157544749519e-05f, -2.07155761322383869926e-05f, -2.08630775118898238230e-05f, -2.09954253799722451970e-05f, -2.11128413898529763743e-05f, -2.12155523986690494219e-05f, -2.13037902431062623279e-05f, -2.13777915154254429865e-05f, -2.14377973398712400380e-05f, -2.14840531495946086989e-05f, -2.15168084642204138245e-05f, -2.15363166681862903517e-05f, -2.15428347899803886579e-05f, -2.15366232824005027408e-05f, -2.15179458039577262574e-05f, -2.14870690015422825217e-05f, -2.14442622944694083432e-05f, -2.13897976600211963056e-05f, -2.13239494205938423077e-05f, -2.12469940325636042692e-05f, -2.11592098769767039874e-05f, -2.10608770521689021838e-05f, -2.09522771684173563140e-05f, -2.08336931447246940868e-05f, -2.07054090078323760555e-05f, -2.05677096935581607638e-05f, -2.04208808505498906150e-05f, -2.02652086465450858026e-05f, -2.01009795772225878085e-05f, -1.99284802777307897448e-05f, -1.97479973369733553508e-05f, -1.95598171147313057406e-05f, -1.93642255616965921713e-05f, -1.91615080424908975211e-05f, -1.89519491617392180451e-05f, -1.87358325932659067312e-05f, -1.85134409124775290410e-05f, -1.82850554319945745078e-05f, -1.80509560405904254164e-05f, -1.78114210454942761765e-05f, -1.75667270181107023892e-05f, -1.73171486432067083623e-05f, -1.70629585716134836285e-05f, -1.68044272764881711800e-05f, -1.65418229131769521309e-05f, -1.62754111827191523261e-05f, -1.60054551990284544996e-05f, -1.57322153597849753270e-05f, -1.54559492210684566130e-05f, -1.51769113757617390406e-05f, -1.48953533357483810943e-05f, -1.46115234179280960335e-05f, -1.43256666340692667526e-05f, -1.40380245845161517285e-05f, -1.37488353557644599467e-05f, -1.34583334219182035850e-05f, -1.31667495500361276861e-05f, -1.28743107093750131593e-05f, -1.25812399845332226092e-05f, -1.22877564924967183814e-05f, -1.19940753035859587103e-05f, -1.17004073663007361519e-05f, -1.14069594360560160192e-05f, -1.11139340078022052344e-05f, -1.08215292525168970740e-05f, -1.05299389575568482923e-05f, -1.02393524708539662493e-05f, -9.94995464893742348120e-06f, -9.66192580876271611343e-06f, -9.37544168332491989210e-06f, -9.09067338103246418157e-06f, -8.80778734881422739372e-06f, -8.52694533893248795902e-06f, -8.24830437947059051977e-06f, -7.97201674846333152320e-06f, -7.69822995163491404110e-06f, -7.42708670370925303302e-06f, -7.15872491325360909230e-06f, -6.89327767101633304933e-06f, -6.63087324171573705124e-06f, -6.37163505923899323223e-06f, -6.11568172520321447157e-06f, -5.86312701083429459631e-06f, -5.61407986211404469277e-06f, -5.36864440814655013182e-06f, -5.12691997269171846217e-06f, -4.88900108881444233564e-06f, -4.65497751659457443244e-06f, -4.42493426384305196255e-06f, -4.19895160976678087265e-06f, -3.97710513152554464493e-06f, -3.75946573362158312445e-06f, -3.54609968006129461300e-06f, -3.33706862922910230812e-06f, -3.13242967141053148128e-06f, -2.93223536890220278037e-06f, -2.73653379864365903313e-06f, -2.54536859730841838172e-06f, -2.35877900878616331091e-06f, -2.17679993399191934131e-06f, -1.99946198293410626841e-06f, -1.82679152897453787459e-06f, -1.65881076521146377169e-06f, -1.49553776291791911430e-06f, -1.33698653196547020166e-06f, -1.18316708316432299485e-06f, -1.03408549244911473199e-06f, -8.89743966841013678409e-07f, -7.50140912114827378233e-07f, -6.15271002100918799020e-07f, -4.85125249549975873449e-07f, -3.59691078491283933177e-07f, -2.38952398011216803052e-07f, -1.22889677382464548894e-07f, 0 // Need a final zero coefficient openmsx-0.10.0/src/sound/WavAudioInput.cc0000644000175000017500000000440112262345041021030 0ustar manuelmanuel00000000000000#include "WavAudioInput.hh" #include "CommandController.hh" #include "PlugException.hh" #include "FilenameSetting.hh" #include "CliComm.hh" #include "serialize.hh" #include "memory.hh" using std::string; namespace openmsx { WavAudioInput::WavAudioInput(CommandController& commandController) : audioInputFilenameSetting(make_unique( commandController, "audio-inputfilename", "filename of the file where the sampler reads data from", "audio-input.wav")) , reference(EmuTime::zero) { audioInputFilenameSetting->attach(*this); } WavAudioInput::~WavAudioInput() { audioInputFilenameSetting->detach(*this); } void WavAudioInput::loadWave() { wav = WavData(audioInputFilenameSetting->getString(), 16, 0); } const string& WavAudioInput::getName() const { static const string name("wavinput"); return name; } string_ref WavAudioInput::getDescription() const { return "Read .wav files. Can for example be used as input for " "samplers."; } void WavAudioInput::plugHelper(Connector& /*connector*/, EmuTime::param time) { try { if (wav.getSize() == 0) { loadWave(); } } catch (MSXException& e) { throw PlugException("Load of wave file failed: " + e.getMessage()); } reference = time; } void WavAudioInput::unplugHelper(EmuTime::param /*time*/) { } void WavAudioInput::update(const Setting& setting) { (void)setting; assert(&setting == audioInputFilenameSetting.get()); try { if (isPluggedIn()) { loadWave(); } } catch (MSXException& e) { // TODO proper error handling, message should go to console setting.getCommandController().getCliComm().printWarning( "Load of wave file failed: " + e.getMessage()); } } short WavAudioInput::readSample(EmuTime::param time) { if (wav.getSize()) { unsigned pos = (time - reference).getTicksAt(wav.getFreq()); if (pos < wav.getSize()) { auto buf = static_cast(wav.getData()); return buf[pos]; } } return 0; } template void WavAudioInput::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("reference", reference); if (ar.isLoader()) { update(*audioInputFilenameSetting); } } INSTANTIATE_SERIALIZE_METHODS(WavAudioInput); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, WavAudioInput, "WavAudioInput"); } // namespace openmsx openmsx-0.10.0/src/sound/MSXTurboRPCM.hh0000644000175000017500000000172112262345041020452 0ustar manuelmanuel00000000000000#ifndef MSXTURBORPCM_HH #define MSXTURBORPCM_HH #include "MSXDevice.hh" #include "Clock.hh" #include namespace openmsx { class MSXMixer; class AudioInputConnector; class DACSound8U; class MSXTurboRPCM : public MSXDevice { public: explicit MSXTurboRPCM(const DeviceConfig& config); virtual ~MSXTurboRPCM(); virtual void reset(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte getSample(EmuTime::param time) const; bool getComp(EmuTime::param time) const; void hardwareMute(bool mute); MSXMixer& mixer; const std::unique_ptr connector; const std::unique_ptr dac; Clock<15750> reference; byte DValue; byte status; byte hold; bool hwMute; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/ResampleBlip.cc0000644000175000017500000000633112262345041020654 0ustar manuelmanuel00000000000000#include "ResampleBlip.hh" #include "ResampledSoundDevice.hh" #include "likely.hh" #include "vla.hh" #include "build-info.hh" #include #include namespace openmsx { template ResampleBlip::ResampleBlip( ResampledSoundDevice& input_, const DynamicClock& hostClock_, unsigned emuSampleRate) : input(input_) , hostClock(hostClock_) , emuClock(hostClock.getTime(), emuSampleRate) , step(FP::roundRatioDown(hostClock.getFreq(), emuSampleRate)) { for (unsigned ch = 0; ch < CHANNELS; ++ch) { lastInput[ch] = 0; } } template bool ResampleBlip::generateOutput(int* dataOut, unsigned hostNum, EmuTime::param time) { unsigned emuNum = emuClock.getTicksTill(time); if (emuNum > 0) { // 3 extra for padding, CHANNELS extra for sentinel // Clang will produce a link error if the length expression is put // inside the macro. const unsigned len = emuNum * CHANNELS + std::max(3u, CHANNELS); VLA_SSE_ALIGNED(int, buf, len); EmuTime emu1 = emuClock.getFastAdd(1); // time of 1st emu-sample assert(emu1 > hostClock.getTime()); if (input.generateInput(buf, emuNum)) { FP pos1; hostClock.getTicksTill(emu1, pos1); for (unsigned ch = 0; ch < CHANNELS; ++ch) { // In case of PSG (and to a lesser degree SCC) it happens // very often that two consecutive samples have the same // value. We can benefit from this by setting a sentinel // at the end of the buffer and move the end-of-loop test // into the 'samples differ' branch. assert(emuNum > 0); buf[CHANNELS * emuNum + ch] = buf[CHANNELS * (emuNum - 1) + ch] + 1; FP pos = pos1; int last = lastInput[ch]; // local var is slightly faster for (unsigned i = 0; /**/; ++i) { int delta = buf[CHANNELS * i + ch] - last; if (unlikely(delta != 0)) { if (i == emuNum) { break; } last = buf[CHANNELS * i + ch]; blip[ch].addDelta( BlipBuffer::TimeIndex(pos), delta); } pos += step; } lastInput[ch] = last; } } else { // input all zero BlipBuffer::TimeIndex pos; hostClock.getTicksTill(emu1, pos); for (unsigned ch = 0; ch < CHANNELS; ++ch) { if (lastInput[ch] != 0) { int delta = -lastInput[ch]; lastInput[ch] = 0; blip[ch].addDelta(pos, delta); } } } emuClock += emuNum; assert(emuClock.getTime() <= time); assert(emuClock.getFastAdd(1) > time); } bool results[CHANNELS]; for (unsigned ch = 0; ch < CHANNELS; ++ch) { results[ch] = blip[ch].template readSamples(dataOut + ch, hostNum); } static_assert((CHANNELS == 1) || (CHANNELS == 2), "either mono or stereo"); bool result; if (CHANNELS == 1) { result = results[0]; } else { if (results[0] == results[1]) { // Both muted or both unmuted result = results[0]; } else { // One channel muted, the other not. // We have to set the muted channel to all-zero. unsigned offset = results[0] ? 1 : 0; for (unsigned i = 0; i < hostNum; ++i) { dataOut[2 * i + offset] = 0; } result = true; } } return result; } // Force template instantiation. template class ResampleBlip<1>; template class ResampleBlip<2>; } // namespace openmsx openmsx-0.10.0/src/sound/SoundDevice.hh0000644000175000017500000001255012262345041020517 0ustar manuelmanuel00000000000000#ifndef SOUNDDEVICE_HH #define SOUNDDEVICE_HH #include "EmuTime.hh" #include "noncopyable.hh" #include "string_ref.hh" #include namespace openmsx { class MSXMixer; class DeviceConfig; class EmuDuration; class Wav16Writer; class Filename; class DynamicClock; class SoundDevice : private noncopyable { public: static const unsigned MAX_CHANNELS = 24; /** Get the unique name that identifies this sound device. * Used to create setting names. */ const std::string& getName() const; /** Gets a description of this sound device, * to be presented to the user. */ const std::string& getDescription() const; /** Is this a stereo device? * This is set in the constructor and cannot be changed anymore */ bool isStereo() const; /** Get extra amplification factor for this device. * Normally the outputBuffer() method should scale the output to * the range [-32768,32768]. Some devices can be emulated slightly * faster to produce another output range. In later stages the output * is anyway still multiplied by some factor. This method tells which * factor should be used to scale the output to the correct range. * The default implementation returns 1. */ virtual int getAmplificationFactor() const; void recordChannel(unsigned channel, const Filename& filename); void muteChannel (unsigned channel, bool muted); protected: /** Constructor. * @param mixer The Mixer object * @param name Name for this device, will be made unique * @param description Description for this sound device * @param numChannels The number of channels for this device * @param stereo Is this a stereo device */ SoundDevice(MSXMixer& mixer, string_ref name, string_ref description, unsigned numChannels, bool stereo = false); virtual ~SoundDevice(); /** * Registers this sound device with the Mixer. * Call this method when the sound device is ready to start receiving * calls to updateBuffer, so after all initialisation is done. * @param config Configuration data for this sound device. */ void registerSound(const DeviceConfig& config); /** * Unregisters this sound device with the Mixer. * Call this method before any deallocation is done. */ void unregisterSound(); /** @see Mixer::updateStream */ void updateStream(EmuTime::param time); void setInputRate(unsigned sampleRate); unsigned getInputRate() const { return inputSampleRate; } public: // Will be called by Mixer: /** * When a SoundDevice registers itself with the Mixer, the Mixer sets * the required sampleRate through this method. All sound devices share * a common sampleRate. */ virtual void setOutputRate(unsigned sampleRate) = 0; /** Generate sample data * @param length The number of required samples * @param buffer This buffer should be filled * @param time current time * @result false iff the output is empty. IOW filling the buffer with * zeros or returning false has the same effect, but the latter * can be more efficient * * This method is regularly called from the Mixer, it should return a * pointer to a buffer filled with the required number of samples. * Samples are always ints, later they are converted to the systems * native format (e.g. 16-bit signed). * * Note: To enable various optimizations (like SSE), this method can * fill the output buffer with up to 3 extra samples. Those extra * samples should be ignored, though the caller must make sure the * buffer has enough space to hold them. */ virtual bool updateBuffer(unsigned length, int* buffer, EmuTime::param time) = 0; protected: /** Abstract method to generate the actual sound data. * @param buffers An array of pointer to buffers. Each buffer must * be big enough to hold 'num' samples. * @param num The number of samples. * * This method should fill each buffer with sound data that * corresponds to one channel of the sound device. The same channel * should each time be written to the same buffer (needed for record). * * If a certain channel is muted it is allowed to set the buffer * pointer to nullptr. This has exactly the same effect as filling the * buffer completely with zeros, but it can be more efficient. */ virtual void generateChannels(int** buffers, unsigned num) = 0; /** Calls generateChannels() and combines the output to a single * channel. * @param dataOut Output buffer, must be big enough to hold * 'num' samples * @param num The number of samples * @result true iff at least one channel was unmuted * * Note: To enable various optimizations (like SSE), this method can * fill the output buffer with up to 3 extra samples. Those extra * samples should be ignored, though the caller must make sure the * buffer has enough space to hold them. */ bool mixChannels(int* dataOut, unsigned num); /** See MSXMixer::getHostSampleClock(). */ const DynamicClock& getHostSampleClock() const; double getEffectiveSpeed() const; private: MSXMixer& mixer; const std::string name; const std::string description; std::unique_ptr writer[MAX_CHANNELS]; unsigned inputSampleRate; const unsigned numChannels; const unsigned stereo; unsigned numRecordChannels; int channelBalance[MAX_CHANNELS]; bool channelMuted[MAX_CHANNELS]; bool balanceCenter; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/ResampledSoundDevice.cc0000644000175000017500000000444512262345041022346 0ustar manuelmanuel00000000000000#include "ResampledSoundDevice.hh" #include "ResampleTrivial.hh" #include "ResampleHQ.hh" #include "ResampleLQ.hh" #include "ResampleBlip.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" #include "GlobalSettings.hh" #include "EnumSetting.hh" #include "unreachable.hh" #include "memory.hh" #include namespace openmsx { ResampledSoundDevice::ResampledSoundDevice( MSXMotherBoard& motherBoard, string_ref name, string_ref description, unsigned channels, bool stereo) : SoundDevice(motherBoard.getMSXMixer(), name, description, channels, stereo) , resampleSetting(motherBoard.getReactor().getGlobalSettings().getResampleSetting()) { resampleSetting.attach(*this); } ResampledSoundDevice::~ResampledSoundDevice() { resampleSetting.detach(*this); } void ResampledSoundDevice::setOutputRate(unsigned /*sampleRate*/) { createResampler(); } bool ResampledSoundDevice::updateBuffer(unsigned length, int* buffer, EmuTime::param time) { return algo->generateOutput(buffer, length, time); } bool ResampledSoundDevice::generateInput(int* buffer, unsigned num) { return mixChannels(buffer, num); } void ResampledSoundDevice::update(const Setting& setting) { (void)setting; assert(&setting == &resampleSetting); createResampler(); } void ResampledSoundDevice::createResampler() { const DynamicClock& hostClock = getHostSampleClock(); unsigned outputRate = hostClock.getFreq(); unsigned inputRate = getInputRate() / getEffectiveSpeed(); if (outputRate == inputRate) { algo = make_unique(*this); } else { switch (resampleSetting.getEnum()) { case RESAMPLE_HQ: if (!isStereo()) { algo = make_unique>( *this, hostClock, inputRate); } else { algo = make_unique>( *this, hostClock, inputRate); } break; case RESAMPLE_LQ: if (!isStereo()) { algo = ResampleLQ<1>::create( *this, hostClock, inputRate); } else { algo = ResampleLQ<2>::create( *this, hostClock, inputRate); } break; case RESAMPLE_BLIP: if (!isStereo()) { algo = make_unique>( *this, hostClock, inputRate); } else { algo = make_unique>( *this, hostClock, inputRate); } break; default: UNREACHABLE; } } } } // namespace openmsx openmsx-0.10.0/src/sound/YM2413Burczynski.hh0000644000175000017500000001653112262345041021235 0ustar manuelmanuel00000000000000#ifndef YM2413BURCZYNSKI_HH #define YM2413BURCZYNSKI_HH #include "YM2413Core.hh" #include "FixedPoint.hh" #include "serialize_meta.hh" namespace openmsx { namespace YM2413Burczynski { class YM2413; class Channel; /** 16.16 fixed point type for frequency calculations. */ typedef FixedPoint<16> FreqIndex; class Slot { public: Slot(); void resetOperators(); /** Update phase increment counter of operator. * Also updates the EG rates if necessary. */ void updateGenerators(Channel& channel); inline int calcOutput(Channel& channel, unsigned eg_cnt, bool carrier, unsigned lfo_am, int phase); inline int calc_slot_mod(Channel& channel, unsigned eg_cnt, bool carrier, unsigned lfo_pm, unsigned lfo_am); inline int calc_envelope(Channel& channel, unsigned eg_cnt, bool carrier); inline int calc_phase(Channel& channel, unsigned lfo_pm); enum KeyPart { KEY_MAIN = 1, KEY_RHYTHM = 2 }; void setKeyOn(KeyPart part); void setKeyOff(KeyPart part); void setKeyOnOff(KeyPart part, bool enabled); /** Does this slot currently produce an output signal? */ bool isActive() const; /** Sets the frequency multiplier [0..15]. */ void setFrequencyMultiplier(byte value); /** Sets the key scale rate: true->0, false->2. */ void setKeyScaleRate(bool value); /** Sets the envelope type of the current instrument. * @param value true->sustained, false->percussive. */ void setEnvelopeSustained(bool value); /** Enables (true) or disables (false) vibrato. */ void setVibrato(bool value); /** Enables (true) or disables (false) amplitude modulation. */ void setAmplitudeModulation(bool value); /** Sets the total level: [0..63]. */ void setTotalLevel(Channel& channel, byte value); /** Sets the key scale level: 0->0 / 1->1.5 / 2->3.0 / 3->6.0 dB/OCT. */ void setKeyScaleLevel(Channel& channel, byte value); /** Sets the waveform: 0 = sinus, 1 = half sinus, half silence. */ void setWaveform(byte value); /** Sets the amount of feedback [0..7]. */ void setFeedbackShift(byte value); /** Sets the attack rate [0..15]. */ void setAttackRate(const Channel& channel, byte value); /** Sets the decay rate [0..15]. */ void setDecayRate(const Channel& channel, byte value); /** Sets the release rate [0..15]. */ void setReleaseRate(const Channel& channel, byte value); /** Sets the sustain level [0..15]. */ void setSustainLevel(byte value); /** Called by Channel when block_fnum changes. */ void updateFrequency(Channel& channel); template void serialize(Archive& ar, unsigned version); public: // public for serialization, otherwise could be private /** Envelope Generator phases * Note: These are ordered: phase constants are compared in the code. */ enum EnvelopeState { EG_DUMP, EG_ATTACK, EG_DECAY, EG_SUSTAIN, EG_RELEASE, EG_OFF }; private: /** Change envelope state */ void setEnvelopeState(EnvelopeState state); inline void updateTotalLevel(Channel& channel); inline void updateAttackRate(int kcodeScaled); inline void updateDecayRate(int kcodeScaled); inline void updateReleaseRate(int kcodeScaled); unsigned* wavetable; // waveform select // Phase Generator FreqIndex phase; // frequency counter FreqIndex freq; // frequency counter step // Envelope Generator int TL; // total level: TL << 2 int TLL; // adjusted now TL int egout; // envelope counter int sl; // sustain level: sl_tab[SL] EnvelopeState state; int op1_out[2]; // MOD output for feedback bool eg_sustain;// percussive/nonpercussive mode byte fb_shift; // feedback shift value byte key; // 0 = KEY OFF, >0 = KEY ON const byte* eg_sel_dp; const byte* eg_sel_ar; const byte* eg_sel_dr; const byte* eg_sel_rr; const byte* eg_sel_rs; unsigned eg_mask_dp; // == (1 << eg_sh_dp) - 1 unsigned eg_mask_ar; // == (1 << eg_sh_ar) - 1 unsigned eg_mask_dr; // == (1 << eg_sh_dr) - 1 unsigned eg_mask_rr; // == (1 << eg_sh_rr) - 1 unsigned eg_mask_rs; // == (1 << eg_sh_rs) - 1 byte eg_sh_dp; // (dump state) byte eg_sh_ar; // (attack state) byte eg_sh_dr; // (decay state) byte eg_sh_rr; // (release state for non-perc.) byte eg_sh_rs; // (release state for perc.mode) byte ar; // attack rate: AR<<2 byte dr; // decay rate: DR<<2 byte rr; // release rate:RR<<2 byte KSR; // key scale rate byte ksl; // keyscale level byte mul; // multiple: mul_tab[ML] // LFO byte AMmask; // LFO Amplitude Modulation enable mask byte vib; // LFO Phase Modulation enable flag (active high) }; class Channel { public: Channel(); /** Calculate the value of the current sample produced by this channel. */ inline int calcOutput(unsigned eg_cnt, unsigned lfo_pm, unsigned lfo_am, int fm); /** Sets the frequency for this channel. */ void setFrequency(int block_fnum); /** Changes the lower 8 bits of the frequency for this channel. */ void setFrequencyLow(byte value); /** Changes the higher 4 bits of the frequency for this channel. */ void setFrequencyHigh(byte value); /** Sets some synthesis parameters as specified by the instrument. * @param part Part [0..7] of the instrument. * @param value New value for this part. */ void updateInstrumentPart(int part, byte value); /** Sets all synthesis parameters as specified by the instrument. * @param inst Instrument data. */ void updateInstrument(const byte* inst); int getBlockFNum() const; FreqIndex getFrequencyIncrement() const; int getKeyScaleLevelBase() const; byte getKeyCode() const; bool isSustained() const; void setSustain(bool sustained); template void serialize(Archive& ar, unsigned version); Slot mod; Slot car; private: // phase generator state int block_fnum; // block+fnum FreqIndex fc; // Freq. freqement base int ksl_base; // KeyScaleLevel Base step bool sus; // sus on/off (release speed in percussive mode) }; class YM2413 : public YM2413Core { public: YM2413(); template void serialize(Archive& ar, unsigned version); private: // YM2413Core virtual void reset(); virtual void writeReg(byte reg, byte value); virtual byte peekReg(byte reg) const; virtual void generateChannels(int* bufs[9 + 5], unsigned num); virtual int getAmplificationFactor() const; /** Reset operator parameters. */ void resetOperators(); inline bool isRhythm() const; Channel& getChannelForReg(byte reg); /** Called when the custom instrument (instrument 0) has changed. * @param part Part [0..7] of the instrument. * @param value The new value. */ void updateCustomInstrument(int part, byte value); void setRhythmFlags(byte old); /** OPLL chips have 9 channels. */ Channel channels[9]; /** Global envelope generator counter. */ unsigned eg_cnt; /** Random generator for noise: 23 bit shift register. */ int noise_rng; /** Number of samples the output was completely silent. */ unsigned idleSamples; typedef FixedPoint< 6> LFOAMIndex; typedef FixedPoint<10> LFOPMIndex; LFOAMIndex lfo_am_cnt; LFOPMIndex lfo_pm_cnt; /** Instrument settings: * 0 - user instrument * 1-15 - fixed instruments * 16 - bass drum settings * 17-18 - other percussion instruments */ byte inst_tab[19][8]; /** Registers */ byte reg[0x40]; }; } // namespace YM2413Burczynski SERIALIZE_CLASS_VERSION(YM2413Burczynski::YM2413, 3); SERIALIZE_CLASS_VERSION(YM2413Burczynski::Channel, 3); SERIALIZE_CLASS_VERSION(YM2413Burczynski::Slot, 2); } // namespace openmsx #endif openmsx-0.10.0/src/sound/Y8950Adpcm.hh0000644000175000017500000000347212262345041020015 0ustar manuelmanuel00000000000000#ifndef Y8950ADPCM_HH #define Y8950ADPCM_HH #include "Y8950.hh" #include "Schedulable.hh" #include "Clock.hh" #include "serialize_meta.hh" #include "openmsx.hh" #include namespace openmsx { class Y8950; class DeviceConfig; class Ram; class Y8950Adpcm : public Schedulable { public: Y8950Adpcm(Y8950& y8950, const DeviceConfig& config, const std::string& name, unsigned sampleRam); virtual ~Y8950Adpcm(); void clearRam(); void reset(EmuTime::param time); bool isMuted() const; void writeReg(byte rg, byte data, EmuTime::param time); byte readReg(byte rg, EmuTime::param time); byte peekReg(byte rg, EmuTime::param time); int calcSample(); void sync(EmuTime::param time); void resetStatus(); template void serialize(Archive& ar, unsigned version); private: // This data is updated while playing struct PlayData { unsigned memPntr; unsigned nowStep; int out; int output; int diff; int nextLeveling; int sampleStep; byte adpcm_data; }; // Schedulable virtual void executeUntil(EmuTime::param time, int userData); void schedule(); void restart(PlayData& pd); bool isPlaying() const; void writeData(byte data); byte peekReg(byte rg) const; byte readData(); byte peekData() const; void writeMemory(unsigned memPntr, byte value); byte readMemory(unsigned memPntr) const; int calcSample(bool doEmu); Y8950& y8950; const std::unique_ptr ram; Clock clock; PlayData emu; // used for emulator behaviour (read back of sample data) PlayData aud; // used by audio generation thread unsigned startAddr; unsigned stopAddr; unsigned addrMask; int volume; int volumeWStep; int readDelay; int delta; byte reg7; byte reg15; bool romBank; }; SERIALIZE_CLASS_VERSION(Y8950Adpcm, 2); } // namespace openmsx #endif openmsx-0.10.0/src/sound/generateBlipTable.cc0000644000175000017500000000625612262345041021654 0ustar manuelmanuel00000000000000#include #include // This defines the constants // BLIP_SAMPLE_BITS, BLIP_PHASE_BITS, BLIP_IMPULSE_WIDTH #include "BlipConfig.hh" int main() { static const int HALF_SIZE = BLIP_RES / 2 * (BLIP_IMPULSE_WIDTH - 1); double fimpulse[HALF_SIZE + 2 * BLIP_RES]; double* out = &fimpulse[BLIP_RES]; // generate sinc, apply hamming window double oversample = ((4.5 / (BLIP_IMPULSE_WIDTH - 1)) + 0.85); double to_angle = M_PI / (2.0 * oversample * BLIP_RES); double to_fraction = M_PI / (2 * (HALF_SIZE - 1)); for (int i = 0; i < HALF_SIZE; ++i) { double angle = ((i - HALF_SIZE) * 2 + 1) * to_angle; out[i] = sin(angle) / angle; out[i] *= 0.54 - 0.46 * cos((2 * i + 1) * to_fraction); } // need mirror slightly past center for calculation for (int i = 0; i < BLIP_RES; ++i) { out[HALF_SIZE + i] = out[HALF_SIZE - 1 - i]; } // starts at 0 for (int i = 0; i < BLIP_RES; ++i) { fimpulse[i] = 0.0; } // find rescale factor double total = 0.0; for (int i = 0; i < HALF_SIZE; ++i) { total += out[i]; } int kernelUnit = 1 << (BLIP_SAMPLE_BITS - 16); double rescale = kernelUnit / (2.0 * total); // integrate, first difference, rescale, convert to int static const int IMPULSES_SIZE = BLIP_RES * (BLIP_IMPULSE_WIDTH / 2) + 1; static int imp[IMPULSES_SIZE]; double sum = 0.0; double next = 0.0; for (int i = 0; i < IMPULSES_SIZE; ++i) { imp[i] = int(floor((next - sum) * rescale + 0.5)); sum += fimpulse[i]; next += fimpulse[i + BLIP_RES]; } // sum pairs for each phase and add error correction to end of first half for (int p = BLIP_RES; p-- >= BLIP_RES / 2; /* */) { int p2 = BLIP_RES - 2 - p; int error = kernelUnit; for (int i = 1; i < IMPULSES_SIZE; i += BLIP_RES) { error -= imp[i + p ]; error -= imp[i + p2]; } if (p == p2) { // phase = 0.5 impulse uses same half for both sides error /= 2; } imp[IMPULSES_SIZE - BLIP_RES + p] += error; } // dump header plus checking code std::cout << "// This is a generated file. DO NOT EDIT!\n"; std::cout << "\n"; std::cout << "// The table was generated for the following constants:\n"; std::cout << "static_assert(BLIP_PHASE_BITS == " << BLIP_PHASE_BITS << ", \"mismatch, regenerate\");\n"; std::cout << "static_assert(BLIP_SAMPLE_BITS == " << BLIP_SAMPLE_BITS << ", \"mismatch, regenerate\");\n"; std::cout << "static_assert(BLIP_IMPULSE_WIDTH == " << BLIP_IMPULSE_WIDTH << ", \"mismatch, regenerate\");\n"; std::cout << "\n"; // dump actual table: reshuffle values to a more cache friendly order std::cout << "static const int impulses[BLIP_RES][BLIP_IMPULSE_WIDTH] = {\n"; for (int phase = 0; phase < BLIP_RES; ++phase) { const int* imp_fwd = &imp[BLIP_RES - phase]; const int* imp_rev = &imp[phase]; std::cout << "\t{ "; for (int i = 0; i < BLIP_IMPULSE_WIDTH / 2; ++i) { std::cout << imp_fwd[BLIP_RES * i] << ", "; } for (int i = BLIP_IMPULSE_WIDTH / 2 - 1; i > 0; --i) { std::cout << imp_rev[BLIP_RES * i] << ", "; } std::cout << imp_rev[BLIP_RES * 0] << " },\n"; } std::cout << "};\n"; } openmsx-0.10.0/src/sound/AY8910Periphery.cc0000644000175000017500000000103012262345041021007 0ustar manuelmanuel00000000000000#include "AY8910Periphery.hh" namespace openmsx { AY8910Periphery::AY8910Periphery() { } AY8910Periphery::~AY8910Periphery() { } byte AY8910Periphery::readA(EmuTime::param /*time*/) { return 0xFF; // unused bits are 1 } byte AY8910Periphery::readB(EmuTime::param /*time*/) { return 0xFF; // unused bits are 1 } void AY8910Periphery::writeA(byte /*value*/, EmuTime::param /*time*/) { // nothing connected } void AY8910Periphery::writeB(byte /*value*/, EmuTime::param /*time*/) { // nothing connected } } // namespace openmsx openmsx-0.10.0/src/sound/WavData.cc0000644000175000017500000000324712262345041017627 0ustar manuelmanuel00000000000000#include "WavData.hh" #include "MSXException.hh" #include "StringOp.hh" #include #include using std::string; namespace openmsx { bool is8Bit(Uint16 format) { return (format == AUDIO_U8) || (format == AUDIO_S8); } WavData::WavData(const string& filename, unsigned wantedBits, unsigned wantedFreq) { SDL_AudioSpec wavSpec; Uint8* wavBuf; Uint32 wavLen; if (!SDL_LoadWAV(filename.c_str(), &wavSpec, &wavBuf, &wavLen)) { throw MSXException(StringOp::Builder() << "WavData error: " << SDL_GetError()); } freq = (wantedFreq == 0) ? unsigned(wavSpec.freq) : wantedFreq; bits = (wantedBits == 0) ? (is8Bit(wavSpec.format) ? 8 : 16) : wantedBits; channels = wavSpec.channels; assert((bits == 8) || (bits == 16)); Uint16 format = (bits == 8) ? AUDIO_U8 : AUDIO_S16SYS; SDL_AudioCVT audioCVT; if (SDL_BuildAudioCVT(&audioCVT, wavSpec.format, wavSpec.channels, wavSpec.freq, format, 1, freq) == -1) { SDL_FreeWAV(wavBuf); throw MSXException("Couldn't build wav converter"); } buffer.resize(wavLen * audioCVT.len_mult); memcpy(buffer.data(), wavBuf, wavLen); SDL_FreeWAV(wavBuf); audioCVT.buf = buffer.data(); audioCVT.len = wavLen; if (SDL_ConvertAudio(&audioCVT) == -1) { throw MSXException("Couldn't convert wav file to internal format"); } length = unsigned(audioCVT.len * audioCVT.len_ratio) / 2; } unsigned WavData::getFreq() const { return freq; } unsigned WavData::getBits() const { return bits; } unsigned WavData::getSize() const { return length; } unsigned WavData::getChannels() const { return channels; } const void* WavData::getData() const { return buffer.data(); } } // namespace openmsx openmsx-0.10.0/src/sound/NullSoundDriver.cc0000644000175000017500000000053612262345041021375 0ustar manuelmanuel00000000000000#include "NullSoundDriver.hh" namespace openmsx { void NullSoundDriver::mute() { } void NullSoundDriver::unmute() { } unsigned NullSoundDriver::getFrequency() const { return 44100; } unsigned NullSoundDriver::getSamples() const { return 0; } void NullSoundDriver::uploadBuffer(short* /*buffer*/, unsigned /*len*/) { } } // namespace openmsx openmsx-0.10.0/src/sound/KeyClick.cc0000644000175000017500000000117512262345041017774 0ustar manuelmanuel00000000000000#include "KeyClick.hh" #include "DACSound8U.hh" #include "memory.hh" namespace openmsx { KeyClick::KeyClick(const DeviceConfig& config) : dac(make_unique( "keyclick", "1-bit click generator", config)) , status(false) { } KeyClick::~KeyClick() { } void KeyClick::reset(EmuTime::param time) { setClick(false, time); } void KeyClick::setClick(bool newStatus, EmuTime::param time) { if (newStatus != status) { status = newStatus; dac->writeDAC((status ? 0xff : 0x80), time); } } // We don't need a serialize() method, instead the setClick() method // gets called during de-serialization. } // namespace openmsx openmsx-0.10.0/src/sound/ResampleTrivial.hh0000644000175000017500000000064112262345041021410 0ustar manuelmanuel00000000000000#ifndef RESAMPLETRIVIAL_HH #define RESAMPLETRIVIAL_HH #include "ResampleAlgo.hh" namespace openmsx { class ResampledSoundDevice; class ResampleTrivial : public ResampleAlgo { public: ResampleTrivial(ResampledSoundDevice& input); virtual bool generateOutput(int* dataOut, unsigned num, EmuTime::param time); private: ResampledSoundDevice& input; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/DirectXSoundDriver.hh0000644000175000017500000000222312262345041022032 0ustar manuelmanuel00000000000000#ifndef DIRECTXSOUNDDRIVER_HH #define DIRECTXSOUNDDRIVER_HH #ifdef _WIN32 #include "SoundDriver.hh" #include "noncopyable.hh" #ifdef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN // Needed for #endif #define DIRECTSOUND_VERSION 0x0500 #include #include namespace openmsx { class DirectXSoundDriver : public SoundDriver, private noncopyable { public: DirectXSoundDriver(unsigned sampleRate, unsigned bufferSize); virtual ~DirectXSoundDriver(); virtual void mute(); virtual void unmute(); virtual unsigned getFrequency() const; virtual unsigned getSamples() const; virtual void uploadBuffer(short* buffer, unsigned len); private: void dxClear(); int dxCanWrite(unsigned start, unsigned size); void dxWriteOne(short* buffer, unsigned lockSize); enum DxState { DX_SOUND_DISABLED, DX_SOUND_ENABLED, DX_SOUND_RUNNING }; DxState state; unsigned bufferOffset; unsigned bufferSize; unsigned fragmentSize; int skipCount; LPDIRECTSOUNDBUFFER primaryBuffer; LPDIRECTSOUNDBUFFER secondaryBuffer; LPDIRECTSOUND directSound; unsigned frequency; }; } // namespace openmsx #endif // _WIN32 #endif // DIRECTXSOUNDDRIVER_HH openmsx-0.10.0/src/sound/DummyAY8910Periphery.hh0000644000175000017500000000125212262345041022043 0ustar manuelmanuel00000000000000#ifndef DUMMYAY8910PERIPHERY_HH #define DUMMYAY8910PERIPHERY_HH #include "AY8910Periphery.hh" namespace openmsx { class DummyAY8910Periphery : public AY8910Periphery { public: static DummyAY8910Periphery& instance() { static DummyAY8910Periphery oneInstance; return oneInstance; } virtual byte readA(EmuTime::param /*time*/) { return 255; } virtual byte readB(EmuTime::param /*time*/) { return 255; } virtual void writeA(byte /*value*/, EmuTime::param /*time*/) {} virtual void writeB(byte /*value*/, EmuTime::param /*time*/) {} private: DummyAY8910Periphery() {} virtual ~DummyAY8910Periphery() {} }; }; // namespace openmsx #endif openmsx-0.10.0/src/sound/DACSound8U.cc0000644000175000017500000000052412262345041020110 0ustar manuelmanuel00000000000000#include "DACSound8U.hh" namespace openmsx { DACSound8U::DACSound8U(string_ref name, string_ref desc, const DeviceConfig& config) : DACSound16S(name, desc, config) { } void DACSound8U::writeDAC(byte value, EmuTime::param time) { DACSound16S::writeDAC((short(value) - 0x80) << 8, time); } } // namespace openmsx openmsx-0.10.0/src/sound/BlipBuffer.cc0000644000175000017500000000517612262345041020323 0ustar manuelmanuel00000000000000#include "BlipBuffer.hh" #include #include #include namespace openmsx { // This defines the table // static const int impulses[BLIP_RES][BLIP_IMPULSE_WIDTH] = { ... }; #include "BlipTable.ii" BlipBuffer::BlipBuffer() { offset = 0; accum = 0; availSamp = 0; memset(buffer, 0, sizeof(buffer)); } void BlipBuffer::addDelta(TimeIndex time, int delta) { unsigned tmp = time.toInt() + BLIP_IMPULSE_WIDTH; assert(tmp < BUFFER_SIZE); availSamp = std::max(availSamp, tmp); unsigned phase = time.fractAsInt(); unsigned ofst = time.toInt() + offset; if (likely((ofst + BLIP_IMPULSE_WIDTH) <= BUFFER_SIZE)) { for (int i = 0; i < BLIP_IMPULSE_WIDTH; ++i) { buffer[ofst + i] += impulses[phase][i] * delta; } } else { for (int i = 0; i < BLIP_IMPULSE_WIDTH; ++i) { buffer[(ofst + i) & BUFFER_MASK] += impulses[phase][i] * delta; } } } static const int SAMPLE_SHIFT = BLIP_SAMPLE_BITS - 16; static const int BASS_SHIFT = 9; template void BlipBuffer::readSamplesHelper(int* __restrict out, unsigned samples) __restrict { assert((offset + samples) <= BUFFER_SIZE); int acc = accum; unsigned ofst = offset; for (unsigned i = 0; i < samples; ++i) { out[i * PITCH] = acc >> SAMPLE_SHIFT; // Note: the following has different rounding behaviour // for positive and negative numbers! The original // code used 'acc / (1<< BASS_SHIFT)' to avoid this, // but it generates less efficient code. acc -= (acc >> BASS_SHIFT); acc += buffer[ofst]; buffer[ofst] = 0; ++ofst; } accum = acc; offset = ofst & BUFFER_MASK; } template bool BlipBuffer::readSamples(int* __restrict out, unsigned samples) { if (availSamp <= 0) { #ifdef DEBUG // buffer contains all zeros (only check this in debug mode) for (unsigned i = 0; i < BUFFER_SIZE; ++i) { assert(buffer[i] == 0); } #endif if (accum == 0) { // muted return false; } int acc = accum; for (unsigned i = 0; i < samples; ++i) { out[i * PITCH] = acc >> SAMPLE_SHIFT; // See note about rounding above. acc -= (acc >> BASS_SHIFT); acc -= (acc > 0) ? 1 : 0; // make sure acc eventually goes to zero } accum = acc; } else { availSamp -= samples; unsigned t1 = std::min(samples, BUFFER_SIZE - offset); readSamplesHelper(out, t1); if (t1 < samples) { assert(offset == 0); unsigned t2 = samples - t1; assert(t2 < BUFFER_SIZE); readSamplesHelper(&out[t1 * PITCH], t2); } assert(offset < BUFFER_SIZE); } return true; } template bool BlipBuffer::readSamples<1>(int*, unsigned); template bool BlipBuffer::readSamples<2>(int*, unsigned); } // namespace openmsx openmsx-0.10.0/src/sound/SCC.hh0000644000175000017500000000342612262345041016721 0ustar manuelmanuel00000000000000#ifndef SCC_HH #define SCC_HH #include "ResampledSoundDevice.hh" #include "Clock.hh" #include "openmsx.hh" #include namespace openmsx { class MSXMotherBoard; class SCCDebuggable; class SCC : public ResampledSoundDevice { public: enum ChipMode {SCC_Real, SCC_Compatible, SCC_plusmode}; SCC(const std::string& name, const DeviceConfig& config, EmuTime::param time, ChipMode mode = SCC_Real); virtual ~SCC(); // interaction with realCartridge void powerUp(EmuTime::param time); void reset(EmuTime::param time); byte readMem(byte address,EmuTime::param time); byte peekMem(byte address,EmuTime::param time) const; void writeMem(byte address, byte value, EmuTime::param time); void setChipMode(ChipMode newMode); template void serialize(Archive& ar, unsigned version); private: // SoundDevice virtual int getAmplificationFactor() const; virtual void generateChannels(int** bufs, unsigned num); inline int adjust(signed char wav, byte vol); byte readWave(unsigned channel, unsigned address, EmuTime::param time) const; void writeWave(unsigned channel, unsigned offset, byte value); void setDeformReg(byte value, EmuTime::param time); void setDeformRegHelper(byte value); void setFreqVol(unsigned address, byte value, EmuTime::param time); byte getFreqVol(unsigned address) const; static const int CLOCK_FREQ = 3579545; friend class SCCDebuggable; const std::unique_ptr debuggable; Clock deformTimer; ChipMode currentChipMode; signed char wave[5][32]; int volAdjustedWave[5][32]; unsigned incr[5]; unsigned count[5]; unsigned pos[5]; unsigned period[5]; unsigned orgPeriod[5]; int out[5]; byte volume[5]; byte ch_enable; byte deformValue; bool rotate[5]; bool readOnly[5]; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/DACSound16S.hh0000644000175000017500000000145212262345041020200 0ustar manuelmanuel00000000000000// This class implements a 16 bit signed DAC #ifndef DACSOUND16S_HH #define DACSOUND16S_HH #include "SoundDevice.hh" #include "BlipBuffer.hh" namespace openmsx { class DACSound16S : public SoundDevice { public: DACSound16S(string_ref name, string_ref desc, const DeviceConfig& config); virtual ~DACSound16S(); void reset(EmuTime::param time); void writeDAC(short value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: // SoundDevice virtual void setOutputRate(unsigned sampleRate); virtual void generateChannels(int** bufs, unsigned num); virtual bool updateBuffer(unsigned length, int* buffer, EmuTime::param time); BlipBuffer blip; short lastWrittenValue; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/LibAOSoundDriver.hh0000644000175000017500000000117012262345041021416 0ustar manuelmanuel00000000000000#ifndef LIBAOSOUNDDRIVER_HH #define LIBAOSOUNDDRIVER_HH #include "SoundDriver.hh" #include "noncopyable.hh" struct ao_device; namespace openmsx { class LibAOSoundDriver : public SoundDriver, private noncopyable { public: LibAOSoundDriver(unsigned sampleRate, unsigned bufferSize); virtual ~LibAOSoundDriver(); virtual void mute(); virtual void unmute(); virtual unsigned getFrequency() const; virtual unsigned getSamples() const; virtual void uploadBuffer(short* buffer, unsigned len); private: ao_device* device; unsigned sampleRate; unsigned bufferSize; }; } // namespace openmsx #endif // LIBAOSOUNDDRIVER_HH openmsx-0.10.0/src/sound/MSXMixer.cc0000644000175000017500000005110712262345041017752 0ustar manuelmanuel00000000000000#include "MSXMixer.hh" #include "Mixer.hh" #include "SoundDevice.hh" #include "MSXCommandController.hh" #include "InfoTopic.hh" #include "TclObject.hh" #include "ThrottleManager.hh" #include "GlobalSettings.hh" #include "IntegerSetting.hh" #include "StringSetting.hh" #include "BooleanSetting.hh" #include "CommandException.hh" #include "AviRecorder.hh" #include "Filename.hh" #include "CliComm.hh" #include "Math.hh" #include "StringOp.hh" #include "vla.hh" #include "unreachable.hh" #include "memory.hh" #include "build-info.hh" #include #include #include #include using std::remove; using std::string; using std::vector; namespace openmsx { class SoundDeviceInfoTopic : public InfoTopic { public: SoundDeviceInfoTopic(InfoCommand& machineInfoCommand, MSXMixer& mixer); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: MSXMixer& mixer; }; MSXMixer::SoundDeviceInfo::SoundDeviceInfo() { } MSXMixer::SoundDeviceInfo::SoundDeviceInfo(SoundDeviceInfo&& rhs) : defaultVolume (std::move(rhs.defaultVolume)) , volumeSetting (std::move(rhs.volumeSetting)) , balanceSetting (std::move(rhs.balanceSetting)) , channelSettings(std::move(rhs.channelSettings)) , left1 (std::move(rhs.left1)) , right1 (std::move(rhs.right1)) , left2 (std::move(rhs.left2)) , right2 (std::move(rhs.right2)) { } MSXMixer::SoundDeviceInfo& MSXMixer::SoundDeviceInfo::operator=(SoundDeviceInfo&& rhs) { defaultVolume = std::move(rhs.defaultVolume); volumeSetting = std::move(rhs.volumeSetting); balanceSetting = std::move(rhs.balanceSetting); channelSettings = std::move(rhs.channelSettings); left1 = std::move(rhs.left1); right1 = std::move(rhs.right1); left2 = std::move(rhs.left2); right2 = std::move(rhs.right2); return *this; } MSXMixer::SoundDeviceInfo::ChannelSettings::ChannelSettings() { } MSXMixer::SoundDeviceInfo::ChannelSettings::ChannelSettings(ChannelSettings&& rhs) : recordSetting(std::move(rhs.recordSetting)) , muteSetting (std::move(rhs.muteSetting)) { } MSXMixer::SoundDeviceInfo::ChannelSettings& MSXMixer::SoundDeviceInfo::ChannelSettings::operator=(ChannelSettings&& rhs) { recordSetting = std::move(rhs.recordSetting); muteSetting = std::move(rhs.muteSetting); return *this; } MSXMixer::MSXMixer(Mixer& mixer_, Scheduler& scheduler, MSXCommandController& msxCommandController_, GlobalSettings& globalSettings) : Schedulable(scheduler) , mixer(mixer_) , commandController(msxCommandController_) , masterVolume(mixer.getMasterVolume()) , speedSetting(globalSettings.getSpeedSetting()) , throttleManager(globalSettings.getThrottleManager()) , prevTime(getCurrentTime(), 44100) , soundDeviceInfo(make_unique( msxCommandController_.getMachineInfoCommand(), *this)) , recorder(nullptr) , synchronousCounter(0) { hostSampleRate = 44100; fragmentSize = 0; muteCount = 1; unmute(); // calls Mixer::registerMixer() reschedule2(); masterVolume.attach(*this); speedSetting.attach(*this); throttleManager.attach(*this); } MSXMixer::~MSXMixer() { if (recorder) { recorder->stop(); } assert(infos.empty()); throttleManager.detach(*this); speedSetting.detach(*this); masterVolume.detach(*this); mute(); // calls Mixer::unregisterMixer() } void MSXMixer::registerSound(SoundDevice& device, double volume, int balance, unsigned numChannels) { // TODO read volume/balance(mode) from config file const string& name = device.getName(); SoundDeviceInfo info; info.defaultVolume = volume; info.volumeSetting = make_unique( commandController, name + "_volume", "the volume of this sound chip", 75, 0, 100); info.balanceSetting = make_unique( commandController, name + "_balance", "the balance of this sound chip", balance, -100, 100); info.volumeSetting->attach(*this); info.balanceSetting->attach(*this); for (unsigned i = 0; i < numChannels; ++i) { SoundDeviceInfo::ChannelSettings channelSettings; string ch_name = StringOp::Builder() << name << "_ch" << i + 1; channelSettings.recordSetting = make_unique( commandController, ch_name + "_record", "filename to record this channel to", "", Setting::DONT_SAVE); channelSettings.recordSetting->attach(*this); channelSettings.muteSetting = make_unique( commandController, ch_name + "_mute", "sets mute-status of individual sound channels", false, Setting::DONT_SAVE); channelSettings.muteSetting->attach(*this); info.channelSettings.push_back(std::move(channelSettings)); } infos[&device] = std::move(info); device.setOutputRate(getSampleRate()); auto it = infos.find(&device); assert(it != infos.end()); updateVolumeParams(*it); commandController.getCliComm().update(CliComm::SOUNDDEVICE, device.getName(), "add"); } void MSXMixer::unregisterSound(SoundDevice& device) { auto it = infos.find(&device); assert(it != infos.end()); it->second.volumeSetting->detach(*this); it->second.balanceSetting->detach(*this); for (auto& s : it->second.channelSettings) { s.recordSetting->detach(*this); s.muteSetting->detach(*this); } infos.erase(it); commandController.getCliComm().update(CliComm::SOUNDDEVICE, device.getName(), "remove"); } void MSXMixer::setSynchronousMode(bool synchronous) { // TODO ATM synchronous is not used anymore if (synchronous) { ++synchronousCounter; if (synchronousCounter == 1) { setMixerParams(fragmentSize, hostSampleRate); } } else { assert(synchronousCounter > 0); --synchronousCounter; if (synchronousCounter == 0) { setMixerParams(fragmentSize, hostSampleRate); } } } double MSXMixer::getEffectiveSpeed() const { return synchronousCounter ? 1.0 : speedSetting.getInt() / 100.0; } void MSXMixer::updateStream(EmuTime::param time) { unsigned count = prevTime.getTicksTill(time); // call generate() even if count==0 and even if muted short mixBuffer[8192 * 2]; assert(count <= 8192); generate(mixBuffer, time, count); if (!muteCount && fragmentSize) { mixer.uploadBuffer(*this, mixBuffer, count); } if (recorder) { recorder->addWave(count, mixBuffer); } prevTime += count; } void MSXMixer::generate(short* output, EmuTime::param time, unsigned samples) { // The code below is specialized for a lot of cases (before this // routine was _much_ shorter). This is done because this routine // ends up relatively high (top 5) in a profile run. // After these specialization this routine runs about two times // faster for the common cases (mono output or no sound at all). // In total emulation time this gave a speedup of about 2%. VLA(int, stereoBuf, 2 * samples + 3); VLA(int, monoBuf, samples + 3); VLA_SSE_ALIGNED(int, tmpBuf, 2 * samples + 3); static const unsigned HAS_MONO_FLAG = 1; static const unsigned HAS_STEREO_FLAG = 2; unsigned usedBuffers = 0; // FIXME: The Infos should be ordered such that all the mono // devices are handled first for (auto& p : infos) { // When samples==0, call updateBuffer() but skip mixing SoundDevice& device = *p.first; if (device.updateBuffer(samples, tmpBuf, time) && (samples > 0)) { if (!device.isStereo()) { int l1 = p.second.left1; int r1 = p.second.right1; if (l1 == r1) { if (!(usedBuffers & HAS_MONO_FLAG)) { usedBuffers |= HAS_MONO_FLAG; #ifdef __arm__ unsigned dummy1, dummy2, dummy3; asm volatile ( "0:\n\t" "ldmia %[in]!,{r3-r6}\n\t" "mul r3,%[f],r3\n\t" "mul r4,%[f],r4\n\t" "mul r5,%[f],r5\n\t" "mul r6,%[f],r6\n\t" "stmia %[out]!,{r3-r6}\n\t" "subs %[n],%[n],#4\n\t" "bgt 0b\n\t" : [in] "=r" (dummy1) , [out] "=r" (dummy2) , [n] "=r" (dummy3) : "[in]" (tmpBuf) , "[out]" (monoBuf) , "[n]" (samples) , [f] "r" (l1) : "memory", "r3","r4","r5","r6" ); #else for (unsigned i = 0; i < samples; ++i) { int tmp = l1 * tmpBuf[i]; monoBuf[i] = tmp; } #endif } else { #ifdef __arm__ unsigned dummy1, dummy2, dummy3; asm volatile ( "0:\n\t" "ldmia %[in]!,{r3,r4,r5,r6}\n\t" "ldmia %[out],{r8,r9,r10,r12}\n\t" "mla r3,%[f],r3,r8\n\t" "mla r4,%[f],r4,r9\n\t" "mla r5,%[f],r5,r10\n\t" "mla r6,%[f],r6,r12\n\t" "stmia %[out]!,{r3,r4,r5,r6}\n\t" "subs %[n],%[n],#4\n\t" "bgt 0b\n\t" : [in] "=r" (dummy1) , [out] "=r" (dummy2) , [n] "=r" (dummy3) : "[in]" (tmpBuf) , "[out]" (monoBuf) , "[n]" (samples) , [f] "r" (l1) : "memory" , "r3","r4","r5","r6" , "r8","r9","r10","r12" ); #else for (unsigned i = 0; i < samples; ++i) { int tmp = l1 * tmpBuf[i]; monoBuf[i] += tmp; } #endif } } else { if (!(usedBuffers & HAS_STEREO_FLAG)) { usedBuffers |= HAS_STEREO_FLAG; for (unsigned i = 0; i < samples; ++i) { int l = l1 * tmpBuf[i]; int r = r1 * tmpBuf[i]; stereoBuf[2 * i + 0] = l; stereoBuf[2 * i + 1] = r; } } else { for (unsigned i = 0; i < samples; ++i) { int l = l1 * tmpBuf[i]; int r = r1 * tmpBuf[i]; stereoBuf[2 * i + 0] += l; stereoBuf[2 * i + 1] += r; } } } } else { int l1 = p.second.left1; int r1 = p.second.right1; int l2 = p.second.left2; int r2 = p.second.right2; if (!(usedBuffers & HAS_STEREO_FLAG)) { usedBuffers |= HAS_STEREO_FLAG; for (unsigned i = 0; i < samples; ++i) { int in1 = tmpBuf[2 * i + 0]; int in2 = tmpBuf[2 * i + 1]; int l = l1 * in1 + l2 * in2; int r = r1 * in1 + r2 * in2; stereoBuf[2 * i + 0] = l; stereoBuf[2 * i + 1] = r; } } else { for (unsigned i = 0; i < samples; ++i) { int in1 = tmpBuf[2 * i + 0]; int in2 = tmpBuf[2 * i + 1]; int l = l1 * in1 + l2 * in2; int r = r1 * in1 + r2 * in2; stereoBuf[2 * i + 0] += l; stereoBuf[2 * i + 1] += r; } } } } } // DC removal filter // y(n) = x(n) - x(n-1) + R * y(n-1) // R = 1 - (pi*2 * cut-off-frequency / samplerate) // take R = 511/512 // 44100Hz --> cutt-off freq = 14Hz // 22050Hz 7Hz // Note: we divide by 512 iso shift-right by 9 because we want // to round towards zero. switch (usedBuffers) { case 0: // no new input if (samples == 0) break; if ((outLeft == outRight) && (prevLeft == prevRight)) { if ((outLeft == 0) && (prevLeft == 0)) { // output was already zero, after DC-filter // it will still be zero memset(output, 0, 2 * samples * sizeof(short)); } else { // output was not zero, but it was the same // left and right assert(samples > 0); outLeft = -prevLeft + ((511 * outLeft) / 512); prevLeft = 0; short out = Math::clipIntToShort(outLeft); output[0] = out; output[1] = out; for (unsigned j = 1; j < samples; ++j) { outLeft = ((511 * outLeft) / 512); out = Math::clipIntToShort(outLeft); output[2 * j + 0] = out; output[2 * j + 1] = out; } } outRight = outLeft; prevRight = prevLeft; } else { assert(samples > 0); outLeft = -prevLeft + ((511 * outLeft ) / 512); outRight = -prevRight + ((511 * outRight) / 512); prevLeft = 0; prevRight = 0; output[0] = Math::clipIntToShort(outLeft); output[1] = Math::clipIntToShort(outRight); for (unsigned j = 1; j < samples; ++j) { outLeft = ((511 * outLeft) / 512); outRight = ((511 * outRight) / 512); output[2 * j + 0] = Math::clipIntToShort(outLeft); output[2 * j + 1] = Math::clipIntToShort(outRight); } } break; case HAS_MONO_FLAG: // only mono if ((outLeft == outRight) && (prevLeft == prevRight)) { // previous output was also mono #ifdef __arm__ // Note: there are two functional differences in the // asm and c++ code below: // - divide by 512 is replaced by ASR #9 // (different for negative numbers) // - the outLeft variable is set to the clipped value // Though this difference is very small, and we need // the extra speed. unsigned dummy1, dummy2, dummy3, dummy4; asm volatile ( "0:\n\t" "rsb %[o],%[o],%[o],LSL #9\n\t" "rsb %[o],%[p],%[o],ASR #9\n\t" "ldr %[p],[%[in]],#4\n\t" "asrs %[p],%[p],#8\n\t" "add %[o],%[o],%[p]\n\t" "lsls %[t],%[o],#16\n\t" "cmp %[o],%[t],ASR #16\n\t" "it ne\n\t" "subne %[o],%[m],%[o],ASR #31\n\t" "strh %[o],[%[out]],#2\n\t" "strh %[o],[%[out]],#2\n\t" "subs %[n],%[n],#1\n\t" "bne 0b\n\t" : [o] "=r" (outLeft) , [p] "=r" (prevLeft) , [in] "=r" (dummy1) , [out] "=r" (dummy2) , [n] "=r" (dummy3) , [t] "=&r" (dummy4) : "[o]" (outLeft) , "[p]" (prevLeft) , "[in]" (monoBuf) , "[out]" (output) , "[n]" (samples) , [m] "r" (0x7FFF) : "memory" ); #else for (unsigned j = 0; j < samples; ++j) { int mono = monoBuf[j] >> 8; outLeft = mono - prevLeft + ((511 * outLeft) / 512); prevLeft = mono; short out = Math::clipIntToShort(outLeft); output[2 * j + 0] = out; output[2 * j + 1] = out; } #endif outRight = outLeft; prevRight = prevLeft; } else { for (unsigned j = 0; j < samples; ++j) { int mono = monoBuf[j] >> 8; outLeft = mono - prevLeft + ((511 * outLeft) / 512); prevLeft = mono; outRight = mono - prevRight + ((511 * outRight) / 512); prevRight = mono; output[2 * j + 0] = Math::clipIntToShort(outLeft); output[2 * j + 1] = Math::clipIntToShort(outRight); } } break; case HAS_STEREO_FLAG: // only stereo for (unsigned j = 0; j < samples; ++j) { int left = stereoBuf[2 * j + 0] >> 8; int right = stereoBuf[2 * j + 1] >> 8; outLeft = left - prevLeft + ((511 * outLeft) / 512); prevLeft = left; outRight = right - prevRight + ((511 * outRight) / 512); prevRight = right; output[2 * j + 0] = Math::clipIntToShort(outLeft); output[2 * j + 1] = Math::clipIntToShort(outRight); } break; default: // mono + stereo for (unsigned j = 0; j < samples; ++j) { int mono = monoBuf[j] >> 8; int left = (stereoBuf[2 * j + 0] >> 8) + mono; int right = (stereoBuf[2 * j + 1] >> 8) + mono; outLeft = left - prevLeft + ((511 * outLeft) / 512); prevLeft = left; outRight = right - prevRight + ((511 * outRight) / 512); prevRight = right; output[2 * j + 0] = Math::clipIntToShort(outLeft); output[2 * j + 1] = Math::clipIntToShort(outRight); } } } bool MSXMixer::needStereoRecording() const { for (auto& p : infos) { auto& device = *p.first; auto& balance = *p.second.balanceSetting; if (device.isStereo() || balance.getInt() != 0) { return true; } } return false; } void MSXMixer::mute() { if (muteCount == 0) { mixer.unregisterMixer(*this); } ++muteCount; } void MSXMixer::unmute() { --muteCount; if (muteCount == 0) { prevLeft = outLeft = 0; prevRight = outRight = 0; mixer.registerMixer(*this); } } void MSXMixer::reInit() { prevTime.reset(getCurrentTime()); prevTime.setFreq(hostSampleRate / getEffectiveSpeed()); reschedule(); } void MSXMixer::reschedule() { removeSyncPoints(); reschedule2(); } void MSXMixer::reschedule2() { unsigned size = (!muteCount && fragmentSize) ? fragmentSize : 512; setSyncPoint(prevTime.getFastAdd(size)); } void MSXMixer::setMixerParams(unsigned newFragmentSize, unsigned newSampleRate) { // TODO old code checked that values did actually change, // investigate if this optimization is worth it hostSampleRate = newSampleRate; fragmentSize = newFragmentSize; reInit(); // must come before call to setOutputRate() for (auto& p : infos) { p.first->setOutputRate(newSampleRate); } } const DynamicClock& MSXMixer::getHostSampleClock() const { return prevTime; } void MSXMixer::setRecorder(AviRecorder* newRecorder) { if ((recorder != nullptr) != (newRecorder != nullptr)) { setSynchronousMode(newRecorder != nullptr); } recorder = newRecorder; } unsigned MSXMixer::getSampleRate() const { return hostSampleRate; } void MSXMixer::update(const Setting& setting) { if (&setting == &masterVolume) { updateMasterVolume(); } else if (&setting == &speedSetting) { if (synchronousCounter == 0) { setMixerParams(fragmentSize, hostSampleRate); } else { // Avoid calling reInit() while recording because // each call causes a small hiccup in the sound (and // while recording this call anyway has no effect). // This was noticable while sliding the speed slider // in catapult (becuase this causes many changes in // the speed setting). } } else if (dynamic_cast(&setting)) { auto it = infos.begin(); while (it != infos.end() && it->second.volumeSetting.get() != &setting && it->second.balanceSetting.get() != &setting) { ++it; } assert(it != infos.end()); updateVolumeParams(*it); } else if (dynamic_cast(&setting)) { changeRecordSetting(setting); } else if (dynamic_cast(&setting)) { changeMuteSetting(setting); } else { UNREACHABLE; } } void MSXMixer::changeRecordSetting(const Setting& setting) { for (auto& p : infos) { unsigned channel = 0; for (auto& s : p.second.channelSettings) { if (s.recordSetting.get() == &setting) { p.first->recordChannel( channel, Filename(s.recordSetting->getString())); return; } ++channel; } } UNREACHABLE; } void MSXMixer::changeMuteSetting(const Setting& setting) { for (auto& p : infos) { unsigned channel = 0; for (auto& s : p.second.channelSettings) { if (s.muteSetting.get() == &setting) { p.first->muteChannel( channel, s.muteSetting->getBoolean()); return; } ++channel; } } UNREACHABLE; } void MSXMixer::update(const ThrottleManager& /*throttleManager*/) { //reInit(); // TODO Should this be removed? } void MSXMixer::updateVolumeParams(Infos::value_type& p) { auto& info = p.second; int mVolume = masterVolume.getInt(); int dVolume = info.volumeSetting->getInt(); double volume = info.defaultVolume * mVolume * dVolume / (100.0 * 100.0); int balance = info.balanceSetting->getInt(); double l1, r1, l2, r2; if (p.first->isStereo()) { if (balance < 0) { double b = (balance + 100.0) / 100.0; l1 = volume; r1 = 0.0; l2 = volume * sqrt(std::max(0.0, 1.0 - b)); r2 = volume * sqrt(std::max(0.0, b)); } else { double b = balance / 100.0; l1 = volume * sqrt(std::max(0.0, 1.0 - b)); r1 = volume * sqrt(std::max(0.0, b)); l2 = 0.0; r2 = volume; } } else { // make sure that in case of rounding errors // we don't take sqrt() of negative numbers double b = (balance + 100.0) / 200.0; l1 = volume * sqrt(std::max(0.0, 1.0 - b)); r1 = volume * sqrt(std::max(0.0, b)); l2 = r2 = 0.0; // dummy } int amp = 256 * p.first->getAmplificationFactor(); info.left1 = int(l1 * amp); info.right1 = int(r1 * amp); info.left2 = int(l2 * amp); info.right2 = int(r2 * amp); } void MSXMixer::updateMasterVolume() { for (auto& p : infos) { updateVolumeParams(p); } } void MSXMixer::executeUntil(EmuTime::param time, int /*userData*/) { updateStream(time); reschedule2(); } // Sound device info SoundDevice* MSXMixer::findDevice(string_ref name) const { for (auto& p : infos) { if (p.first->getName() == name) { return p.first; } } return nullptr; } SoundDeviceInfoTopic::SoundDeviceInfoTopic( InfoCommand& machineInfoCommand, MSXMixer& mixer_) : InfoTopic(machineInfoCommand, "sounddevice") , mixer(mixer_) { } void SoundDeviceInfoTopic::execute(const vector& tokens, TclObject& result) const { switch (tokens.size()) { case 2: for (auto& p : mixer.infos) { result.addListElement(p.first->getName()); } break; case 3: { SoundDevice* device = mixer.findDevice(tokens[2].getString()); if (!device) { throw CommandException("Unknown sound device"); } result.setString(device->getDescription()); break; } default: throw CommandException("Too many parameters"); } } string SoundDeviceInfoTopic::help(const vector& /*tokens*/) const { return "Shows a list of available sound devices.\n"; } void SoundDeviceInfoTopic::tabCompletion(vector& tokens) const { if (tokens.size() == 3) { vector devices; for (auto& p : mixer.infos) { devices.push_back(p.first->getName()); } completeString(tokens, devices); } } } // namespace openmsx openmsx-0.10.0/src/sound/DummyY8950KeyboardDevice.hh0000644000175000017500000000075312262345041022664 0ustar manuelmanuel00000000000000#ifndef DUMMYY8950KEYBOARDDEVICE_HH #define DUMMYY8950KEYBOARDDEVICE_HH #include "Y8950KeyboardDevice.hh" namespace openmsx { class DummyY8950KeyboardDevice : public Y8950KeyboardDevice { public: virtual void write(byte data, EmuTime::param time); virtual byte read(EmuTime::param time); virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/DACSound16S.cc0000644000175000017500000000351612262345041020171 0ustar manuelmanuel00000000000000#include "DACSound16S.hh" #include "DeviceConfig.hh" #include "MSXMotherBoard.hh" #include "DynamicClock.hh" #include "serialize.hh" namespace openmsx { DACSound16S::DACSound16S(string_ref name, string_ref desc, const DeviceConfig& config) : SoundDevice(config.getMotherBoard().getMSXMixer(), name, desc, 1) , lastWrittenValue(0) { registerSound(config); } DACSound16S::~DACSound16S() { unregisterSound(); } void DACSound16S::setOutputRate(unsigned sampleRate) { setInputRate(sampleRate); } void DACSound16S::reset(EmuTime::param time) { writeDAC(0, time); } void DACSound16S::writeDAC(short value, EmuTime::param time) { int delta = value - lastWrittenValue; if (delta == 0) return; lastWrittenValue = value; BlipBuffer::TimeIndex t; getHostSampleClock().getTicksTill(time, t); blip.addDelta(t, delta); } void DACSound16S::generateChannels(int** bufs, unsigned num) { // Note: readSamples() replaces the values in the buffer. It should add // to the existing values in the buffer. But because there is only // one channel this doesn't matter (buffer contains all zeros). if (!blip.readSamples<1>(bufs[0], num)) { bufs[0] = nullptr; } } bool DACSound16S::updateBuffer(unsigned length, int* buffer, EmuTime::param /*time*/) { return mixChannels(buffer, length); } template void DACSound16S::serialize(Archive& ar, unsigned /*version*/) { // Note: It's ok to NOT serialize a DAC object if you call the // writeDAC() method in some other way during de-serialization. // This is for example done in MSXPPI/KeyClick. short lastValue = lastWrittenValue; ar.serialize("lastValue", lastValue); if (ar.isLoader()) { writeDAC(lastValue, getHostSampleClock().getTime()); } } INSTANTIATE_SERIALIZE_METHODS(DACSound16S); } // namespace openmsx openmsx-0.10.0/src/sound/ResampleLQ.hh0000644000175000017500000000277212262345041020321 0ustar manuelmanuel00000000000000#ifndef RESAMPLELQ_HH #define RESAMPLELQ_HH #include "ResampleAlgo.hh" #include "DynamicClock.hh" #include "FixedPoint.hh" #include namespace openmsx { class ResampledSoundDevice; template class ResampleLQ : public ResampleAlgo { public: static std::unique_ptr> create( ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate); protected: ResampleLQ(ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate); bool fetchData(EmuTime::param time, unsigned& valid); ResampledSoundDevice& input; const DynamicClock& hostClock; DynamicClock emuClock; typedef FixedPoint<14> FP; const FP step; int lastInput[2 * CHANNELS]; }; template class ResampleLQDown : public ResampleLQ { public: ResampleLQDown(ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate); private: virtual bool generateOutput(int* dataOut, unsigned num, EmuTime::param time); typedef typename ResampleLQ::FP FP; }; template class ResampleLQUp : public ResampleLQ { public: ResampleLQUp(ResampledSoundDevice& input, const DynamicClock& hostClock, unsigned emuSampleRate); private: virtual bool generateOutput(int* dataOut, unsigned num, EmuTime::param time); typedef typename ResampleLQ::FP FP; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXMoonSound.hh0000644000175000017500000000207712262345041020623 0ustar manuelmanuel00000000000000#ifndef MSXMOONSOUND_HH #define MSXMOONSOUND_HH #include "MSXDevice.hh" #include "serialize_meta.hh" #include namespace openmsx { class YMF262; class YMF278; class MSXMoonSound : public MSXDevice { public: explicit MSXMoonSound(const DeviceConfig& config); virtual ~MSXMoonSound(); virtual void powerUp(EmuTime::param time); virtual void reset(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: bool getNew2() const; byte readYMF278Status(EmuTime::param time) const; const std::unique_ptr ymf262; const std::unique_ptr ymf278; /** Time at which instrument loading is finished. */ EmuTime ymf278LoadTime; /** Time until which the YMF278 is busy. */ EmuTime ymf278BusyTime; int opl3latch; byte opl4latch; bool alreadyReadID; }; SERIALIZE_CLASS_VERSION(MSXMoonSound, 3); } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXSCCPlusCart.cc0000644000175000017500000001735712262345041020765 0ustar manuelmanuel00000000000000// Note: this device is actually called SCC-I. But this would take a lot of // renaming, which isn't worth it right now. TODO rename this :) #include "MSXSCCPlusCart.hh" #include "RomBlockDebuggable.hh" #include "SCC.hh" #include "Ram.hh" #include "File.hh" #include "FileContext.hh" #include "FileException.hh" #include "XMLElement.hh" #include "MSXMotherBoard.hh" #include "CacheLine.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { MSXSCCPlusCart::MSXSCCPlusCart(const DeviceConfig& config) : MSXDevice(config) , ram(make_unique( config, getName() + " RAM", "SCC+ RAM", 0x20000)) , scc(make_unique( getName(), config, getCurrentTime(), SCC::SCC_Compatible)) , romBlockDebug(make_unique( *this, mapper, 0x4000, 0x8000, 13)) { if (const XMLElement* fileElem = config.findChild("filename")) { // read the rom file const std::string& filename = fileElem->getData(); try { File file(config.getFileContext().resolve(filename)); auto size = std::min(file.getSize(), ram->getSize()); file.read(&(*ram)[0], size); } catch (FileException&) { throw MSXException("Error reading file: " + filename); } } string_ref subtype = config.getChildData("subtype", "expanded"); if (subtype == "Snatcher") { mapperMask = 0x0F; lowRAM = true; highRAM = false; } else if (subtype == "SD-Snatcher") { mapperMask = 0x0F; lowRAM = false; highRAM = true; } else if (subtype == "mirrored") { mapperMask = 0x07; lowRAM = true; highRAM = true; } else { // subtype "expanded", and all others mapperMask = 0x0F; lowRAM = true; highRAM = true; } // make valgrind happy for (int i = 0; i < 4; ++i) { isRamSegment[i] = true; mapper[i] = 0; } powerUp(getCurrentTime()); } MSXSCCPlusCart::~MSXSCCPlusCart() { } void MSXSCCPlusCart::powerUp(EmuTime::param time) { scc->powerUp(time); reset(time); } void MSXSCCPlusCart::reset(EmuTime::param time) { setModeRegister(0); setMapper(0, 0); setMapper(1, 1); setMapper(2, 2); setMapper(3, 3); scc->reset(time); } byte MSXSCCPlusCart::readMem(word addr, EmuTime::param time) { byte result; if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) || ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) { result = scc->readMem(addr & 0xFF, time); } else { result = MSXSCCPlusCart::peekMem(addr, time); } return result; } byte MSXSCCPlusCart::peekMem(word addr, EmuTime::param time) const { // modeRegister can not be read! if (((enable == EN_SCC) && (0x9800 <= addr) && (addr < 0xA000)) || ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) { // SCC visible in 0x9800 - 0x9FFF // SCC+ visible in 0xB800 - 0xBFFF return scc->peekMem(addr & 0xFF, time); } else if ((0x4000 <= addr) && (addr < 0xC000)) { // SCC(+) enabled/disabled but not requested so memory stuff return internalMemoryBank[(addr >> 13) - 2][addr & 0x1FFF]; } else { // outside memory range return 0xFF; } } const byte* MSXSCCPlusCart::getReadCacheLine(word start) const { if (((enable == EN_SCC) && (0x9800 <= start) && (start < 0xA000)) || ((enable == EN_SCCPLUS) && (0xB800 <= start) && (start < 0xC000))) { // SCC visible in 0x9800 - 0x9FFF // SCC+ visible in 0xB800 - 0xBFFF return nullptr; } else if ((0x4000 <= start) && (start < 0xC000)) { // SCC(+) enabled/disabled but not requested so memory stuff return &internalMemoryBank[(start >> 13) - 2][start & 0x1FFF]; } else { // outside memory range return unmappedRead; } } void MSXSCCPlusCart::writeMem(word address, byte value, EmuTime::param time) { if ((address < 0x4000) || (0xC000 <= address)) { // outside memory range return; } // Mode register is mapped upon 0xBFFE and 0xBFFF if ((address | 0x0001) == 0xBFFF) { setModeRegister(value); return; } // Write to RAM int regio = (address >> 13) - 2; if (isRamSegment[regio]) { // According to Sean Young // when the regio's are in RAM mode you can read from // the SCC(+) but not write to them // => we assume a write to the memory but maybe // they are just discarded // TODO check this out => ask Sean... if (isMapped[regio]) { internalMemoryBank[regio][address & 0x1FFF] = value; } return; } /* Write to bankswitching registers * The address to change banks: * bank 1: 0x5000 - 0x57FF (0x5000 used) * bank 2: 0x7000 - 0x77FF (0x7000 used) * bank 3: 0x9000 - 0x97FF (0x9000 used) * bank 4: 0xB000 - 0xB7FF (0xB000 used) */ if ((address & 0x1800) == 0x1000) { setMapper(regio, value); return; } // call writeMemInterface of SCC if needed switch (enable) { case EN_NONE: // do nothing break; case EN_SCC: if ((0x9800 <= address) && (address < 0xA000)) { scc->writeMem(address & 0xFF, value, time); } break; case EN_SCCPLUS: if ((0xB800 <= address) && (address < 0xC000)) { scc->writeMem(address & 0xFF, value, time); } break; } } byte* MSXSCCPlusCart::getWriteCacheLine(word start) const { if ((0x4000 <= start) && (start < 0xC000)) { if (start == (0xBFFF & CacheLine::HIGH)) { return nullptr; } int regio = (start >> 13) - 2; if (isRamSegment[regio] && isMapped[regio]) { return &internalMemoryBank[regio][start & 0x1FFF]; } return nullptr; } return unmappedWrite; } void MSXSCCPlusCart::setMapper(int regio, byte value) { mapper[regio] = value; value &= mapperMask; byte* block; if ((!lowRAM && (value < 8)) || (!highRAM && (value >= 8))) { block = unmappedRead; isMapped[regio] = false; } else { block = &(*ram)[0x2000 * value]; isMapped[regio] = true; } checkEnable(); internalMemoryBank[regio] = block; invalidateMemCache(0x4000 + regio * 0x2000, 0x2000); } void MSXSCCPlusCart::setModeRegister(byte value) { modeRegister = value; checkEnable(); if (modeRegister & 0x20) { scc->setChipMode(SCC::SCC_plusmode); } else { scc->setChipMode(SCC::SCC_Compatible); } if (modeRegister & 0x10) { isRamSegment[0] = true; isRamSegment[1] = true; isRamSegment[2] = true; isRamSegment[3] = true; } else { if (modeRegister & 0x01) { isRamSegment[0] = true; } else { if (isRamSegment[0]) { invalidateMemCache(0x4000, 0x2000); isRamSegment[0] = false; } } if (modeRegister & 0x02) { isRamSegment[1] = true; } else { if (isRamSegment[1]) { invalidateMemCache(0x6000, 0x2000); isRamSegment[1] = false; } } if ((modeRegister & 0x24) == 0x24) { // extra requirement for this bank: SCC+ mode isRamSegment[2] = true; } else { if (isRamSegment[2]) { invalidateMemCache(0x8000, 0x2000); isRamSegment[2] = false; } } if (isRamSegment[3]) { invalidateMemCache(0xA000, 0x2000); isRamSegment[3] = false; } } } void MSXSCCPlusCart::checkEnable() { if ((modeRegister & 0x20) && (mapper[3] & 0x80)) { enable = EN_SCCPLUS; } else if ((!(modeRegister & 0x20)) && ((mapper[2] & 0x3F) == 0x3F)) { enable = EN_SCC; } else { enable = EN_NONE; } } template void MSXSCCPlusCart::serialize(Archive& ar, unsigned /*version*/) { // These are constants: // mapperMask, lowRAM, highRAM // only serialize that part of the Ram object that's actually // present in the cartridge unsigned ramSize = (lowRAM && highRAM && (mapperMask == 0xF)) ? 0x20000 : 0x10000; unsigned ramBase = lowRAM ? 0x00000 : 0x10000; ar.serialize_blob("ram", &(*ram)[ramBase], ramSize); ar.serialize("scc", *scc); ar.serialize("mapper", mapper); ar.serialize("mode", modeRegister); if (ar.isLoader()) { // recalculate: isMapped[4], internalMemoryBank[4] for (int i = 0; i < 4; ++i) { setMapper(i, mapper[i]); } // recalculate: enable, isRamSegment[4] setModeRegister(modeRegister); } } INSTANTIATE_SERIALIZE_METHODS(MSXSCCPlusCart); REGISTER_MSXDEVICE(MSXSCCPlusCart, "SCCPlus"); } // namespace openmsx openmsx-0.10.0/src/sound/ResampleTrivial.cc0000644000175000017500000000073312262345041021400 0ustar manuelmanuel00000000000000#include "ResampleTrivial.hh" #include "ResampledSoundDevice.hh" #include namespace openmsx { ResampleTrivial::ResampleTrivial(ResampledSoundDevice& input_) : input(input_) { } bool ResampleTrivial::generateOutput(int* dataOut, unsigned num, EmuTime::param /*time*/) { #ifdef __SSE2__ assert((long(dataOut) & 15) == 0); // must be 16-byte aligned #endif return input.generateInput(dataOut, num); } } // namespace openmsx openmsx-0.10.0/src/sound/AY8910Periphery.hh0000644000175000017500000000257312262345041021036 0ustar manuelmanuel00000000000000#ifndef AY8910PERIPHERY_HH #define AY8910PERIPHERY_HH #include "EmuTime.hh" #include "openmsx.hh" namespace openmsx { /** Models the general purpose I/O ports of the AY8910. * The default implementation handles an empty periphery: * nothing is connected to the I/O ports. * This class can be overridden to connect peripherals. */ class AY8910Periphery { public: /** Reads the state of the peripheral on port A. * Since the AY8910 doesn't have control lines for the I/O ports, * a peripheral is not aware that it is read, which means that * "peek" and "read" are equivalent. * @param time The moment in time the peripheral's state is read. * On subsequent calls, the time will always be increasing. * @return the value read; unconnected bits should be 1 */ virtual byte readA(EmuTime::param time); /** Similar to readA, but reads port B. */ virtual byte readB(EmuTime::param time); /** Writes to the peripheral on port A. * @param value The value to write. * @param time The moment in time the value is written. * On subsequent calls, the time will always be increasing. */ virtual void writeA(byte value, EmuTime::param time); /** Similar to writeA, but writes port B. */ virtual void writeB(byte value, EmuTime::param time); protected: AY8910Periphery(); virtual ~AY8910Periphery(); }; } // namespace openmsx #endif // AY8910PERIPHERY_HH openmsx-0.10.0/src/sound/MSXAudio.hh0000644000175000017500000000275612262345041017747 0ustar manuelmanuel00000000000000#ifndef MSXAUDIO_HH #define MSXAUDIO_HH #include "MSXDevice.hh" #include #include namespace openmsx { class Y8950; class Y8950Periphery; class DACSound8U; class MSXAudio : public MSXDevice { public: explicit MSXAudio(const DeviceConfig& config); virtual ~MSXAudio(); /** Creates a periphery object for this MSXAudio cartridge. * The ownership of the object remains with the MSXAudio instance. */ Y8950Periphery& createPeriphery(const std::string& soundDeviceName); virtual void powerUp(EmuTime::param time); virtual void reset(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte* getWriteCacheLine(word start) const; template void serialize(Archive& ar, unsigned version); private: void enableDAC(bool enable, EmuTime::param time); std::unique_ptr periphery; std::unique_ptr y8950; std::unique_ptr dac; int registerLatch; byte dacValue; bool dacEnabled; friend class MusicModulePeriphery; friend class PanasonicAudioPeriphery; friend class ToshibaAudioPeriphery; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/BlipTable.ii0000644000175000017500000025156312262345041020160 0ustar manuelmanuel00000000000000// This is a generated file. DO NOT EDIT! // The table was generated for the following constants: static_assert(BLIP_PHASE_BITS == 10, "mismatch, regenerate"); static_assert(BLIP_SAMPLE_BITS == 29, "mismatch, regenerate"); static_assert(BLIP_IMPULSE_WIDTH == 16, "mismatch, regenerate"); static const int impulses[BLIP_RES][BLIP_IMPULSE_WIDTH] = { { 3, -25, 94, -229, 428, -688, 1310, 6406, 1310, -688, 428, -229, 94, -25, 3, 0 }, { 3, -25, 94, -229, 427, -686, 1304, 6406, 1316, -689, 428, -229, 94, -25, 3, 0 }, { 3, -25, 94, -229, 427, -684, 1298, 6404, 1323, -691, 428, -229, 94, -24, 3, 0 }, { 3, -25, 94, -229, 426, -682, 1292, 6405, 1329, -693, 429, -229, 93, -24, 3, 0 }, { 3, -25, 94, -229, 426, -681, 1285, 6406, 1335, -694, 429, -229, 93, -24, 3, 0 }, { 3, -25, 94, -229, 425, -679, 1279, 6406, 1341, -696, 430, -229, 93, -24, 3, 0 }, { 3, -25, 95, -229, 425, -677, 1273, 6404, 1348, -698, 430, -229, 93, -24, 3, 0 }, { 3, -25, 95, -229, 424, -676, 1267, 6404, 1354, -699, 431, -229, 93, -24, 3, 0 }, { 3, -26, 95, -229, 424, -674, 1261, 6405, 1360, -701, 431, -229, 93, -24, 3, 0 }, { 3, -26, 95, -229, 423, -672, 1255, 6405, 1366, -702, 432, -229, 93, -24, 2, 0 }, { 3, -26, 95, -229, 423, -671, 1249, 6406, 1373, -704, 432, -229, 92, -24, 2, 0 }, { 4, -26, 95, -229, 422, -669, 1242, 6406, 1379, -706, 432, -229, 92, -23, 2, 0 }, { 4, -26, 95, -229, 422, -667, 1236, 6404, 1385, -707, 433, -229, 92, -23, 2, 0 }, { 4, -26, 95, -229, 421, -666, 1230, 6406, 1391, -709, 433, -229, 92, -23, 2, 0 }, { 4, -26, 96, -229, 421, -664, 1224, 6403, 1398, -711, 434, -229, 92, -23, 2, 0 }, { 4, -26, 96, -229, 420, -662, 1218, 6403, 1404, -712, 434, -229, 92, -23, 2, 0 }, { 4, -26, 96, -229, 420, -660, 1212, 6402, 1410, -714, 435, -229, 92, -23, 2, 0 }, { 4, -26, 96, -229, 419, -659, 1206, 6405, 1416, -716, 435, -229, 91, -23, 2, 0 }, { 4, -27, 96, -229, 419, -657, 1200, 6404, 1423, -717, 435, -229, 91, -23, 2, 0 }, { 4, -27, 96, -229, 418, -655, 1193, 6403, 1429, -719, 436, -229, 91, -22, 2, 1 }, { 4, -27, 96, -229, 418, -654, 1187, 6403, 1435, -720, 436, -229, 91, -22, 2, 1 }, { 4, -27, 96, -229, 417, -652, 1181, 6402, 1442, -722, 437, -229, 91, -22, 2, 1 }, { 4, -27, 96, -229, 417, -650, 1175, 6401, 1448, -724, 437, -228, 91, -22, 2, 1 }, { 4, -27, 97, -229, 416, -648, 1169, 6400, 1454, -725, 438, -228, 91, -22, 1, 1 }, { 4, -27, 97, -229, 416, -647, 1163, 6401, 1461, -727, 438, -228, 90, -22, 1, 1 }, { 4, -27, 97, -228, 415, -645, 1157, 6400, 1467, -728, 438, -228, 90, -22, 1, 1 }, { 4, -27, 97, -228, 415, -643, 1151, 6399, 1473, -730, 439, -228, 90, -22, 1, 1 }, { 4, -27, 97, -228, 414, -641, 1145, 6399, 1480, -732, 439, -228, 90, -22, 1, 1 }, { 4, -28, 97, -228, 413, -640, 1139, 6399, 1486, -733, 440, -228, 90, -21, 1, 1 }, { 4, -28, 97, -228, 413, -638, 1133, 6399, 1492, -735, 440, -228, 90, -21, 1, 1 }, { 4, -28, 97, -228, 412, -636, 1127, 6398, 1499, -736, 440, -228, 90, -21, 1, 1 }, { 4, -28, 97, -228, 412, -634, 1121, 6398, 1505, -738, 441, -228, 89, -21, 1, 1 }, { 4, -28, 97, -228, 411, -633, 1115, 6399, 1511, -739, 441, -228, 89, -21, 1, 1 }, { 4, -28, 98, -228, 411, -631, 1109, 6397, 1518, -741, 441, -228, 89, -21, 1, 1 }, { 4, -28, 98, -228, 410, -629, 1103, 6397, 1524, -743, 442, -228, 89, -21, 1, 1 }, { 4, -28, 98, -228, 410, -627, 1097, 6395, 1531, -744, 442, -228, 89, -21, 1, 1 }, { 5, -28, 98, -228, 409, -626, 1091, 6394, 1537, -746, 443, -228, 89, -20, 1, 1 }, { 5, -28, 98, -228, 409, -624, 1085, 6395, 1543, -747, 443, -228, 88, -20, 0, 1 }, { 5, -29, 98, -228, 408, -622, 1079, 6396, 1550, -749, 443, -228, 88, -20, 0, 1 }, { 5, -29, 98, -228, 407, -620, 1073, 6395, 1556, -750, 444, -228, 88, -20, 0, 1 }, { 5, -29, 98, -228, 407, -618, 1067, 6393, 1563, -752, 444, -227, 88, -20, 0, 1 }, { 5, -29, 98, -228, 406, -617, 1061, 6394, 1569, -753, 444, -227, 88, -20, 0, 1 }, { 5, -29, 98, -228, 406, -615, 1055, 6393, 1575, -755, 445, -227, 88, -20, 0, 1 }, { 5, -29, 99, -228, 405, -613, 1049, 6393, 1582, -757, 445, -227, 87, -20, 0, 1 }, { 5, -29, 99, -227, 405, -611, 1043, 6390, 1588, -758, 445, -227, 87, -19, 0, 1 }, { 5, -29, 99, -227, 404, -610, 1037, 6390, 1595, -760, 446, -227, 87, -19, 0, 1 }, { 5, -29, 99, -227, 403, -608, 1031, 6390, 1601, -761, 446, -227, 87, -19, 0, 1 }, { 5, -29, 99, -227, 403, -606, 1025, 6389, 1608, -763, 446, -227, 87, -19, 0, 1 }, { 5, -29, 99, -227, 402, -604, 1019, 6388, 1614, -764, 447, -227, 87, -19, 0, 1 }, { 5, -30, 99, -227, 402, -602, 1014, 6388, 1621, -766, 447, -227, 86, -19, 0, 1 }, { 5, -30, 99, -227, 401, -601, 1008, 6389, 1627, -767, 447, -227, 86, -19, 0, 1 }, { 5, -30, 99, -227, 400, -599, 1002, 6390, 1633, -769, 448, -227, 86, -19, -1, 1 }, { 5, -30, 99, -227, 400, -597, 996, 6386, 1640, -770, 448, -226, 86, -18, -1, 1 }, { 5, -30, 99, -227, 399, -595, 990, 6387, 1646, -772, 448, -226, 86, -18, -1, 1 }, { 5, -30, 99, -227, 399, -593, 984, 6384, 1653, -773, 449, -226, 86, -18, -1, 1 }, { 5, -30, 100, -227, 398, -592, 978, 6386, 1659, -775, 449, -226, 85, -18, -1, 1 }, { 5, -30, 100, -226, 398, -590, 972, 6382, 1666, -776, 449, -226, 85, -18, -1, 2 }, { 5, -30, 100, -226, 397, -588, 967, 6381, 1672, -778, 450, -226, 85, -18, -1, 2 }, { 5, -30, 100, -226, 396, -586, 961, 6380, 1679, -779, 450, -226, 85, -18, -1, 2 }, { 5, -30, 100, -226, 396, -584, 955, 6380, 1685, -781, 450, -226, 85, -18, -1, 2 }, { 5, -31, 100, -226, 395, -582, 949, 6379, 1692, -782, 451, -226, 84, -17, -1, 2 }, { 5, -31, 100, -226, 394, -581, 943, 6380, 1698, -783, 451, -226, 84, -17, -1, 2 }, { 5, -31, 100, -226, 394, -579, 937, 6378, 1705, -785, 451, -225, 84, -17, -1, 2 }, { 6, -31, 100, -226, 393, -577, 932, 6376, 1711, -786, 451, -225, 84, -17, -1, 2 }, { 6, -31, 100, -226, 393, -575, 926, 6375, 1718, -788, 452, -225, 84, -17, -2, 2 }, { 6, -31, 100, -226, 392, -573, 920, 6375, 1724, -789, 452, -225, 84, -17, -2, 2 }, { 6, -31, 100, -225, 391, -571, 914, 6375, 1731, -791, 452, -225, 83, -17, -2, 2 }, { 6, -31, 100, -225, 391, -570, 908, 6373, 1737, -792, 453, -225, 83, -16, -2, 2 }, { 6, -31, 100, -225, 390, -568, 903, 6372, 1744, -794, 453, -225, 83, -16, -2, 2 }, { 6, -31, 101, -225, 389, -566, 897, 6370, 1751, -795, 453, -225, 83, -16, -2, 2 }, { 6, -31, 101, -225, 389, -564, 891, 6368, 1757, -796, 453, -224, 83, -16, -2, 2 }, { 6, -31, 101, -225, 388, -562, 885, 6368, 1764, -798, 454, -224, 82, -16, -2, 2 }, { 6, -32, 101, -225, 388, -560, 880, 6367, 1770, -799, 454, -224, 82, -16, -2, 2 }, { 6, -32, 101, -225, 387, -559, 874, 6368, 1777, -801, 454, -224, 82, -16, -2, 2 }, { 6, -32, 101, -225, 386, -557, 868, 6368, 1783, -802, 454, -224, 82, -16, -2, 2 }, { 6, -32, 101, -224, 386, -555, 862, 6364, 1790, -804, 455, -224, 82, -15, -2, 2 }, { 6, -32, 101, -224, 385, -553, 857, 6364, 1796, -805, 455, -224, 81, -15, -2, 2 }, { 6, -32, 101, -224, 384, -551, 851, 6363, 1803, -806, 455, -224, 81, -15, -2, 2 }, { 6, -32, 101, -224, 384, -549, 845, 6362, 1810, -808, 455, -223, 81, -15, -3, 2 }, { 6, -32, 101, -224, 383, -548, 840, 6361, 1816, -809, 456, -223, 81, -15, -3, 2 }, { 6, -32, 101, -224, 382, -546, 834, 6361, 1823, -811, 456, -223, 81, -15, -3, 2 }, { 6, -32, 101, -224, 382, -544, 828, 6361, 1829, -812, 456, -223, 80, -15, -3, 2 }, { 6, -32, 101, -224, 381, -542, 823, 6358, 1836, -813, 456, -223, 80, -14, -3, 2 }, { 6, -32, 101, -223, 380, -540, 817, 6356, 1843, -815, 457, -223, 80, -14, -3, 2 }, { 6, -33, 101, -223, 380, -538, 811, 6356, 1849, -816, 457, -223, 80, -14, -3, 2 }, { 6, -33, 102, -223, 379, -536, 806, 6352, 1856, -817, 457, -222, 80, -14, -3, 2 }, { 6, -33, 102, -223, 378, -535, 800, 6355, 1862, -819, 457, -222, 79, -14, -3, 2 }, { 6, -33, 102, -223, 378, -533, 794, 6353, 1869, -820, 457, -222, 79, -14, -3, 2 }, { 6, -33, 102, -223, 377, -531, 789, 6350, 1876, -821, 458, -222, 79, -14, -3, 2 }, { 6, -33, 102, -223, 376, -529, 783, 6350, 1882, -823, 458, -222, 79, -13, -3, 2 }, { 6, -33, 102, -223, 376, -527, 777, 6348, 1889, -824, 458, -222, 79, -13, -3, 2 }, { 6, -33, 102, -222, 375, -525, 772, 6346, 1896, -825, 458, -221, 78, -13, -4, 2 }, { 6, -33, 102, -222, 374, -523, 766, 6346, 1902, -827, 458, -221, 78, -13, -4, 3 }, { 7, -33, 102, -222, 374, -521, 761, 6341, 1909, -828, 459, -221, 78, -13, -4, 3 }, { 7, -33, 102, -222, 373, -520, 755, 6342, 1915, -829, 459, -221, 78, -13, -4, 3 }, { 7, -33, 102, -222, 372, -518, 749, 6342, 1922, -831, 459, -221, 78, -13, -4, 3 }, { 7, -33, 102, -222, 372, -516, 744, 6339, 1929, -832, 459, -221, 77, -12, -4, 3 }, { 7, -34, 102, -221, 371, -514, 738, 6338, 1935, -833, 459, -220, 77, -12, -4, 3 }, { 7, -34, 102, -221, 370, -512, 733, 6336, 1942, -835, 460, -220, 77, -12, -4, 3 }, { 7, -34, 102, -221, 370, -510, 727, 6334, 1949, -836, 460, -220, 77, -12, -4, 3 }, { 7, -34, 102, -221, 369, -508, 722, 6334, 1955, -837, 460, -220, 76, -12, -4, 3 }, { 7, -34, 102, -221, 368, -506, 716, 6334, 1962, -839, 460, -220, 76, -12, -4, 3 }, { 7, -34, 102, -221, 368, -504, 711, 6330, 1969, -840, 460, -219, 76, -12, -4, 3 }, { 7, -34, 102, -221, 367, -503, 705, 6330, 1975, -841, 460, -219, 76, -11, -4, 3 }, { 7, -34, 102, -220, 366, -501, 700, 6327, 1982, -842, 461, -219, 76, -11, -5, 3 }, { 7, -34, 102, -220, 365, -499, 694, 6328, 1989, -844, 461, -219, 75, -11, -5, 3 }, { 7, -34, 102, -220, 365, -497, 689, 6326, 1995, -845, 461, -219, 75, -11, -5, 3 }, { 7, -34, 103, -220, 364, -495, 683, 6324, 2002, -846, 461, -219, 75, -11, -5, 3 }, { 7, -34, 103, -220, 363, -493, 678, 6322, 2009, -848, 461, -218, 75, -11, -5, 3 }, { 7, -34, 103, -220, 363, -491, 672, 6321, 2016, -849, 461, -218, 74, -11, -5, 3 }, { 7, -34, 103, -219, 362, -489, 667, 6318, 2022, -850, 461, -218, 74, -10, -5, 3 }, { 7, -34, 103, -219, 361, -487, 661, 6316, 2029, -851, 462, -218, 74, -10, -5, 3 }, { 7, -35, 103, -219, 360, -486, 656, 6316, 2036, -853, 462, -217, 74, -10, -5, 3 }, { 7, -35, 103, -219, 360, -484, 650, 6315, 2042, -854, 462, -217, 74, -10, -5, 3 }, { 7, -35, 103, -219, 359, -482, 645, 6314, 2049, -855, 462, -217, 73, -10, -5, 3 }, { 7, -35, 103, -219, 358, -480, 639, 6313, 2056, -856, 462, -217, 73, -10, -5, 3 }, { 7, -35, 103, -218, 358, -478, 634, 6309, 2063, -857, 462, -217, 73, -10, -5, 3 }, { 7, -35, 103, -218, 357, -476, 629, 6308, 2069, -859, 462, -216, 73, -9, -6, 3 }, { 7, -35, 103, -218, 356, -474, 623, 6308, 2076, -860, 462, -216, 72, -9, -6, 3 }, { 7, -35, 103, -218, 355, -472, 618, 6305, 2083, -861, 463, -216, 72, -9, -6, 3 }, { 7, -35, 103, -218, 355, -470, 612, 6304, 2089, -862, 463, -216, 72, -9, -6, 3 }, { 7, -35, 103, -217, 354, -468, 607, 6301, 2096, -863, 463, -216, 72, -9, -6, 3 }, { 7, -35, 103, -217, 353, -466, 602, 6300, 2103, -865, 463, -215, 71, -9, -6, 3 }, { 7, -35, 103, -217, 352, -465, 596, 6300, 2110, -866, 463, -215, 71, -9, -6, 3 }, { 7, -35, 103, -217, 352, -463, 591, 6297, 2116, -867, 463, -215, 71, -8, -6, 3 }, { 8, -35, 103, -217, 351, -461, 586, 6294, 2123, -868, 463, -215, 71, -8, -6, 3 }, { 8, -35, 103, -216, 350, -459, 580, 6292, 2130, -869, 463, -214, 70, -8, -6, 3 }, { 8, -36, 103, -216, 349, -457, 575, 6292, 2137, -871, 463, -214, 70, -8, -6, 3 }, { 8, -36, 103, -216, 349, -455, 569, 6291, 2143, -872, 463, -214, 70, -8, -6, 3 }, { 8, -36, 103, -216, 348, -453, 564, 6288, 2150, -873, 463, -214, 70, -8, -6, 4 }, { 8, -36, 103, -216, 347, -451, 559, 6285, 2157, -874, 464, -214, 70, -7, -7, 4 }, { 8, -36, 103, -215, 346, -449, 554, 6282, 2164, -875, 464, -213, 69, -7, -7, 4 }, { 8, -36, 103, -215, 346, -447, 548, 6281, 2170, -876, 464, -213, 69, -7, -7, 4 }, { 8, -36, 103, -215, 345, -445, 543, 6279, 2177, -877, 464, -213, 69, -7, -7, 4 }, { 8, -36, 103, -215, 344, -443, 538, 6278, 2184, -879, 464, -213, 69, -7, -7, 4 }, { 8, -36, 103, -215, 343, -441, 532, 6277, 2191, -880, 464, -212, 68, -7, -7, 4 }, { 8, -36, 103, -214, 343, -440, 527, 6274, 2198, -881, 464, -212, 68, -7, -7, 4 }, { 8, -36, 103, -214, 342, -438, 522, 6272, 2204, -882, 464, -212, 68, -6, -7, 4 }, { 8, -36, 103, -214, 341, -436, 517, 6270, 2211, -883, 464, -212, 68, -6, -7, 4 }, { 8, -36, 103, -214, 340, -434, 511, 6269, 2218, -884, 464, -211, 67, -6, -7, 4 }, { 8, -36, 103, -214, 340, -432, 506, 6266, 2225, -885, 464, -211, 67, -6, -7, 4 }, { 8, -36, 103, -213, 339, -430, 501, 6264, 2231, -886, 464, -211, 67, -6, -7, 4 }, { 8, -36, 103, -213, 338, -428, 496, 6262, 2238, -887, 464, -210, 67, -6, -8, 4 }, { 8, -36, 103, -213, 337, -426, 490, 6262, 2245, -889, 464, -210, 66, -5, -8, 4 }, { 8, -37, 103, -213, 337, -424, 485, 6260, 2252, -890, 464, -210, 66, -5, -8, 4 }, { 8, -37, 103, -213, 336, -422, 480, 6258, 2259, -891, 464, -210, 66, -5, -8, 4 }, { 8, -37, 103, -212, 335, -420, 475, 6256, 2265, -892, 464, -209, 65, -5, -8, 4 }, { 8, -37, 103, -212, 334, -418, 470, 6254, 2272, -893, 464, -209, 65, -5, -8, 4 }, { 8, -37, 103, -212, 333, -416, 465, 6252, 2279, -894, 464, -209, 65, -5, -8, 4 }, { 8, -37, 103, -212, 333, -414, 459, 6249, 2286, -895, 464, -209, 65, -4, -8, 4 }, { 8, -37, 103, -211, 332, -413, 454, 6247, 2293, -896, 464, -208, 64, -4, -8, 4 }, { 8, -37, 103, -211, 331, -411, 449, 6245, 2300, -897, 464, -208, 64, -4, -8, 4 }, { 8, -37, 103, -211, 330, -409, 444, 6244, 2306, -898, 464, -208, 64, -4, -8, 4 }, { 8, -37, 103, -211, 330, -407, 439, 6241, 2313, -899, 464, -208, 64, -4, -8, 4 }, { 8, -37, 103, -211, 329, -405, 434, 6239, 2320, -900, 464, -207, 63, -4, -8, 4 }, { 8, -37, 103, -210, 328, -403, 429, 6237, 2327, -901, 464, -207, 63, -4, -9, 4 }, { 8, -37, 103, -210, 327, -401, 423, 6235, 2334, -902, 464, -207, 63, -3, -9, 4 }, { 8, -37, 103, -210, 326, -399, 418, 6233, 2340, -903, 464, -206, 63, -3, -9, 4 }, { 8, -37, 103, -210, 326, -397, 413, 6231, 2347, -904, 464, -206, 62, -3, -9, 4 }, { 8, -37, 103, -209, 325, -395, 408, 6228, 2354, -905, 464, -206, 62, -3, -9, 4 }, { 8, -37, 103, -209, 324, -393, 403, 6225, 2361, -906, 464, -205, 62, -3, -9, 4 }, { 9, -37, 103, -209, 323, -391, 398, 6222, 2368, -907, 464, -205, 62, -3, -9, 4 }, { 9, -37, 103, -209, 322, -389, 393, 6220, 2375, -908, 464, -205, 61, -2, -9, 4 }, { 9, -38, 103, -208, 322, -387, 388, 6217, 2382, -909, 464, -205, 61, -2, -9, 4 }, { 9, -38, 103, -208, 321, -385, 383, 6215, 2388, -910, 464, -204, 61, -2, -9, 4 }, { 9, -38, 103, -208, 320, -383, 378, 6214, 2395, -911, 464, -204, 60, -2, -9, 4 }, { 9, -38, 103, -208, 319, -382, 373, 6213, 2402, -912, 464, -204, 60, -2, -9, 4 }, { 9, -38, 103, -207, 318, -380, 368, 6209, 2409, -913, 464, -203, 60, -2, -10, 5 }, { 9, -38, 103, -207, 318, -378, 363, 6205, 2416, -914, 464, -203, 60, -1, -10, 5 }, { 9, -38, 103, -207, 317, -376, 358, 6204, 2423, -915, 464, -203, 59, -1, -10, 5 }, { 9, -38, 103, -207, 316, -374, 353, 6202, 2429, -916, 464, -202, 59, -1, -10, 5 }, { 9, -38, 103, -206, 315, -372, 348, 6199, 2436, -917, 464, -202, 59, -1, -10, 5 }, { 9, -38, 103, -206, 314, -370, 343, 6198, 2443, -918, 464, -202, 58, -1, -10, 5 }, { 9, -38, 103, -206, 314, -368, 338, 6195, 2450, -919, 463, -201, 58, -1, -10, 5 }, { 9, -38, 103, -206, 313, -366, 333, 6192, 2457, -920, 463, -201, 58, 0, -10, 5 }, { 9, -38, 103, -205, 312, -364, 328, 6188, 2464, -920, 463, -201, 58, 0, -10, 5 }, { 9, -38, 103, -205, 311, -362, 323, 6187, 2471, -921, 463, -201, 57, 0, -10, 5 }, { 9, -38, 103, -205, 310, -360, 318, 6184, 2478, -922, 463, -200, 57, 0, -10, 5 }, { 9, -38, 103, -205, 309, -358, 313, 6183, 2484, -923, 463, -200, 57, 0, -10, 5 }, { 9, -38, 103, -204, 309, -356, 308, 6179, 2491, -924, 463, -200, 57, 0, -10, 5 }, { 9, -38, 103, -204, 308, -354, 303, 6177, 2498, -925, 463, -199, 56, 1, -11, 5 }, { 9, -38, 103, -204, 307, -352, 298, 6175, 2505, -926, 463, -199, 56, 1, -11, 5 }, { 9, -38, 103, -204, 306, -350, 294, 6172, 2512, -927, 463, -199, 56, 1, -11, 5 }, { 9, -38, 103, -203, 305, -349, 289, 6171, 2519, -928, 462, -198, 55, 1, -11, 5 }, { 9, -38, 103, -203, 304, -347, 284, 6168, 2526, -928, 462, -198, 55, 1, -11, 5 }, { 9, -39, 103, -203, 304, -345, 279, 6165, 2533, -929, 462, -197, 55, 1, -11, 5 }, { 9, -39, 103, -202, 303, -343, 274, 6163, 2539, -930, 462, -197, 54, 2, -11, 5 }, { 9, -39, 103, -202, 302, -341, 269, 6161, 2546, -931, 462, -197, 54, 2, -11, 5 }, { 9, -39, 103, -202, 301, -339, 264, 6158, 2553, -932, 462, -196, 54, 2, -11, 5 }, { 9, -39, 103, -202, 300, -337, 260, 6155, 2560, -933, 462, -196, 54, 2, -11, 5 }, { 9, -39, 103, -201, 299, -335, 255, 6152, 2567, -933, 462, -196, 53, 2, -11, 5 }, { 9, -39, 103, -201, 299, -333, 250, 6149, 2574, -934, 461, -195, 53, 2, -11, 5 }, { 9, -39, 103, -201, 298, -331, 245, 6147, 2581, -935, 461, -195, 53, 3, -12, 5 }, { 9, -39, 103, -201, 297, -329, 240, 6146, 2588, -936, 461, -195, 52, 3, -12, 5 }, { 9, -39, 103, -200, 296, -327, 236, 6141, 2595, -937, 461, -194, 52, 3, -12, 5 }, { 9, -39, 103, -200, 295, -325, 231, 6139, 2601, -937, 461, -194, 52, 3, -12, 5 }, { 9, -39, 103, -200, 294, -323, 226, 6137, 2608, -938, 461, -194, 52, 3, -12, 5 }, { 9, -39, 103, -199, 294, -321, 221, 6133, 2615, -939, 461, -193, 51, 3, -12, 5 }, { 9, -39, 102, -199, 293, -319, 217, 6131, 2622, -940, 460, -193, 51, 4, -12, 5 }, { 9, -39, 102, -199, 292, -317, 212, 6128, 2629, -941, 460, -192, 51, 4, -12, 5 }, { 9, -39, 102, -199, 291, -315, 207, 6126, 2636, -941, 460, -192, 50, 4, -12, 5 }, { 9, -39, 102, -198, 290, -313, 202, 6123, 2643, -942, 460, -192, 50, 4, -12, 5 }, { 9, -39, 102, -198, 289, -312, 198, 6120, 2650, -943, 460, -191, 50, 4, -12, 5 }, { 9, -39, 102, -198, 289, -310, 193, 6118, 2657, -944, 459, -191, 49, 5, -12, 5 }, { 10, -39, 102, -197, 288, -308, 188, 6113, 2664, -944, 459, -191, 49, 5, -12, 5 }, { 10, -39, 102, -197, 287, -306, 184, 6110, 2671, -945, 459, -190, 49, 5, -13, 5 }, { 10, -39, 102, -197, 286, -304, 179, 6109, 2677, -946, 459, -190, 48, 5, -13, 6 }, { 10, -39, 102, -197, 285, -302, 174, 6105, 2684, -946, 459, -189, 48, 5, -13, 6 }, { 10, -39, 102, -196, 284, -300, 170, 6101, 2691, -947, 459, -189, 48, 5, -13, 6 }, { 10, -39, 102, -196, 283, -298, 165, 6099, 2698, -948, 458, -189, 48, 6, -13, 6 }, { 10, -39, 102, -196, 283, -296, 160, 6096, 2705, -949, 458, -188, 47, 6, -13, 6 }, { 10, -39, 102, -195, 282, -294, 156, 6091, 2712, -949, 458, -188, 47, 6, -13, 6 }, { 10, -39, 102, -195, 281, -292, 151, 6089, 2719, -950, 458, -188, 47, 6, -13, 6 }, { 10, -39, 102, -195, 280, -290, 146, 6088, 2726, -951, 457, -187, 46, 6, -13, 6 }, { 10, -40, 102, -194, 279, -288, 142, 6084, 2733, -951, 457, -187, 46, 6, -13, 6 }, { 10, -40, 102, -194, 278, -286, 137, 6080, 2740, -952, 457, -186, 46, 7, -13, 6 }, { 10, -40, 102, -194, 277, -284, 133, 6078, 2747, -953, 457, -186, 45, 7, -13, 6 }, { 10, -40, 102, -194, 276, -282, 128, 6077, 2753, -953, 457, -186, 45, 7, -14, 6 }, { 10, -40, 102, -193, 276, -280, 123, 6073, 2760, -954, 456, -185, 45, 7, -14, 6 }, { 10, -40, 102, -193, 275, -279, 119, 6072, 2767, -955, 456, -185, 44, 7, -14, 6 }, { 10, -40, 102, -193, 274, -277, 114, 6068, 2774, -955, 456, -184, 44, 7, -14, 6 }, { 10, -40, 101, -192, 273, -275, 110, 6064, 2781, -956, 456, -184, 44, 8, -14, 6 }, { 10, -40, 101, -192, 272, -273, 105, 6062, 2788, -956, 455, -183, 43, 8, -14, 6 }, { 10, -40, 101, -192, 271, -271, 101, 6059, 2795, -957, 455, -183, 43, 8, -14, 6 }, { 10, -40, 101, -191, 270, -269, 96, 6056, 2802, -958, 455, -183, 43, 8, -14, 6 }, { 10, -40, 101, -191, 269, -267, 92, 6051, 2809, -958, 455, -182, 43, 8, -14, 6 }, { 10, -40, 101, -191, 269, -265, 87, 6049, 2816, -959, 454, -182, 42, 9, -14, 6 }, { 10, -40, 101, -190, 268, -263, 83, 6044, 2823, -960, 454, -181, 42, 9, -14, 6 }, { 10, -40, 101, -190, 267, -261, 78, 6041, 2830, -960, 454, -181, 42, 9, -14, 6 }, { 10, -40, 101, -190, 266, -259, 74, 6040, 2837, -961, 453, -181, 41, 9, -14, 6 }, { 10, -40, 101, -190, 265, -257, 69, 6038, 2843, -961, 453, -180, 41, 9, -15, 6 }, { 10, -40, 101, -189, 264, -255, 65, 6034, 2850, -962, 453, -180, 41, 9, -15, 6 }, { 10, -40, 101, -189, 263, -253, 60, 6030, 2857, -962, 453, -179, 40, 10, -15, 6 }, { 10, -40, 101, -189, 262, -251, 56, 6028, 2864, -963, 452, -179, 40, 10, -15, 6 }, { 10, -40, 101, -188, 262, -250, 51, 6023, 2871, -963, 452, -178, 40, 10, -15, 6 }, { 10, -40, 101, -188, 261, -248, 47, 6021, 2878, -964, 452, -178, 39, 10, -15, 6 }, { 10, -40, 101, -188, 260, -246, 43, 6019, 2885, -965, 451, -178, 39, 10, -15, 6 }, { 10, -40, 101, -187, 259, -244, 38, 6013, 2892, -965, 451, -177, 39, 11, -15, 6 }, { 10, -40, 100, -187, 258, -242, 34, 6012, 2899, -966, 451, -177, 38, 11, -15, 6 }, { 10, -40, 100, -187, 257, -240, 29, 6009, 2906, -966, 450, -176, 38, 11, -15, 6 }, { 10, -40, 100, -186, 256, -238, 25, 6005, 2913, -967, 450, -176, 38, 11, -15, 6 }, { 10, -40, 100, -186, 255, -236, 21, 6001, 2920, -967, 450, -175, 37, 11, -15, 6 }, { 10, -40, 100, -186, 254, -234, 16, 6001, 2927, -968, 449, -175, 37, 11, -16, 6 }, { 10, -40, 100, -185, 254, -232, 12, 5993, 2934, -968, 449, -174, 37, 12, -16, 6 }, { 10, -40, 100, -185, 253, -230, 8, 5992, 2940, -969, 449, -174, 36, 12, -16, 6 }, { 10, -40, 100, -185, 252, -228, 3, 5989, 2947, -969, 448, -173, 36, 12, -16, 6 }, { 10, -40, 100, -184, 251, -226, -1, 5983, 2954, -969, 448, -173, 36, 12, -16, 7 }, { 10, -40, 100, -184, 250, -224, -5, 5981, 2961, -970, 448, -173, 35, 12, -16, 7 }, { 10, -40, 100, -184, 249, -223, -10, 5978, 2968, -970, 447, -172, 35, 13, -16, 7 }, { 10, -40, 100, -183, 248, -221, -14, 5974, 2975, -971, 447, -172, 35, 13, -16, 7 }, { 10, -40, 100, -183, 247, -219, -18, 5970, 2982, -971, 447, -171, 34, 13, -16, 7 }, { 10, -40, 100, -183, 246, -217, -23, 5969, 2989, -972, 446, -171, 34, 13, -16, 7 }, { 10, -40, 100, -182, 245, -215, -27, 5963, 2996, -972, 446, -170, 34, 13, -16, 7 }, { 10, -40, 99, -182, 245, -213, -31, 5961, 3003, -973, 446, -170, 33, 13, -16, 7 }, { 10, -40, 99, -182, 244, -211, -35, 5956, 3010, -973, 445, -169, 33, 14, -16, 7 }, { 10, -40, 99, -181, 243, -209, -40, 5954, 3017, -973, 445, -169, 32, 14, -17, 7 }, { 10, -40, 99, -181, 242, -207, -44, 5951, 3024, -974, 444, -168, 32, 14, -17, 7 }, { 10, -40, 99, -181, 241, -205, -48, 5947, 3031, -974, 444, -168, 32, 14, -17, 7 }, { 10, -41, 99, -180, 240, -203, -52, 5944, 3037, -974, 444, -167, 31, 14, -17, 7 }, { 10, -41, 99, -180, 239, -201, -56, 5941, 3044, -975, 443, -167, 31, 15, -17, 7 }, { 11, -41, 99, -180, 238, -200, -61, 5937, 3051, -975, 443, -166, 31, 15, -17, 7 }, { 11, -41, 99, -179, 237, -198, -65, 5935, 3058, -976, 442, -166, 30, 15, -17, 7 }, { 11, -41, 99, -179, 236, -196, -69, 5930, 3065, -976, 442, -165, 30, 15, -17, 7 }, { 11, -41, 99, -178, 236, -194, -73, 5924, 3072, -976, 442, -165, 30, 15, -17, 7 }, { 11, -41, 99, -178, 235, -192, -77, 5922, 3079, -977, 441, -164, 29, 15, -17, 7 }, { 11, -41, 99, -178, 234, -190, -82, 5918, 3086, -977, 441, -164, 29, 16, -17, 7 }, { 11, -41, 98, -177, 233, -188, -86, 5915, 3093, -977, 440, -164, 29, 16, -17, 7 }, { 11, -41, 98, -177, 232, -186, -90, 5913, 3100, -978, 440, -163, 28, 16, -18, 7 }, { 11, -41, 98, -177, 231, -184, -94, 5909, 3107, -978, 440, -163, 28, 16, -18, 7 }, { 11, -41, 98, -176, 230, -182, -98, 5904, 3114, -978, 439, -162, 28, 16, -18, 7 }, { 11, -41, 98, -176, 229, -180, -102, 5900, 3121, -978, 439, -162, 27, 17, -18, 7 }, { 11, -41, 98, -176, 228, -179, -106, 5899, 3127, -979, 438, -161, 27, 17, -18, 7 }, { 11, -41, 98, -175, 227, -177, -110, 5894, 3134, -979, 438, -161, 27, 17, -18, 7 }, { 11, -41, 98, -175, 226, -175, -114, 5891, 3141, -979, 437, -160, 26, 17, -18, 7 }, { 11, -41, 98, -175, 226, -173, -118, 5886, 3148, -980, 437, -159, 26, 17, -18, 7 }, { 11, -41, 98, -174, 225, -171, -122, 5882, 3155, -980, 436, -159, 25, 18, -18, 7 }, { 11, -41, 98, -174, 224, -169, -127, 5878, 3162, -980, 436, -158, 25, 18, -18, 7 }, { 11, -41, 98, -173, 223, -167, -131, 5873, 3169, -980, 436, -158, 25, 18, -18, 7 }, { 11, -41, 97, -173, 222, -165, -135, 5872, 3176, -981, 435, -157, 24, 18, -18, 7 }, { 11, -41, 97, -173, 221, -163, -139, 5868, 3183, -981, 435, -157, 24, 18, -18, 7 }, { 11, -41, 97, -172, 220, -162, -143, 5865, 3190, -981, 434, -156, 24, 18, -19, 7 }, { 11, -41, 97, -172, 219, -160, -147, 5861, 3197, -981, 434, -156, 23, 19, -19, 7 }, { 11, -41, 97, -172, 218, -158, -151, 5857, 3204, -981, 433, -155, 23, 19, -19, 7 }, { 11, -41, 97, -171, 217, -156, -155, 5854, 3210, -982, 433, -155, 23, 19, -19, 7 }, { 11, -41, 97, -171, 216, -154, -159, 5851, 3217, -982, 432, -154, 22, 19, -19, 7 }, { 11, -41, 97, -171, 215, -152, -163, 5847, 3224, -982, 432, -154, 22, 19, -19, 7 }, { 11, -41, 97, -170, 215, -150, -166, 5840, 3231, -982, 431, -153, 21, 20, -19, 7 }, { 11, -41, 97, -170, 214, -148, -170, 5836, 3238, -982, 431, -153, 21, 20, -19, 7 }, { 11, -41, 96, -169, 213, -146, -174, 5833, 3245, -983, 430, -152, 21, 20, -19, 7 }, { 11, -41, 96, -169, 212, -145, -178, 5830, 3252, -983, 430, -152, 20, 20, -19, 8 }, { 11, -41, 96, -169, 211, -143, -182, 5826, 3259, -983, 429, -151, 20, 20, -19, 8 }, { 11, -41, 96, -168, 210, -141, -186, 5820, 3266, -983, 429, -151, 20, 21, -19, 8 }, { 11, -41, 96, -168, 209, -139, -190, 5818, 3273, -983, 428, -150, 19, 21, -20, 8 }, { 11, -41, 96, -168, 208, -137, -194, 5813, 3280, -983, 428, -149, 19, 21, -20, 8 }, { 11, -41, 96, -167, 207, -135, -198, 5810, 3286, -983, 427, -149, 19, 21, -20, 8 }, { 11, -41, 96, -167, 206, -133, -202, 5807, 3293, -984, 427, -148, 18, 21, -20, 8 }, { 11, -41, 96, -166, 205, -131, -205, 5801, 3300, -984, 426, -148, 18, 22, -20, 8 }, { 11, -41, 96, -166, 204, -130, -209, 5799, 3307, -984, 425, -147, 17, 22, -20, 8 }, { 11, -41, 96, -166, 203, -128, -213, 5795, 3314, -984, 425, -147, 17, 22, -20, 8 }, { 11, -41, 95, -165, 203, -126, -217, 5790, 3321, -984, 424, -146, 17, 22, -20, 8 }, { 11, -41, 95, -165, 202, -124, -221, 5787, 3328, -984, 424, -146, 16, 22, -20, 8 }, { 11, -41, 95, -165, 201, -122, -225, 5783, 3335, -984, 423, -145, 16, 22, -20, 8 }, { 11, -41, 95, -164, 200, -120, -228, 5776, 3342, -984, 423, -145, 16, 23, -20, 8 }, { 11, -41, 95, -164, 199, -118, -232, 5774, 3348, -984, 422, -144, 15, 23, -20, 8 }, { 11, -41, 95, -163, 198, -117, -236, 5769, 3355, -984, 422, -143, 15, 23, -20, 8 }, { 11, -41, 95, -163, 197, -115, -240, 5768, 3362, -984, 421, -143, 14, 23, -21, 8 }, { 11, -41, 95, -163, 196, -113, -244, 5764, 3369, -984, 420, -142, 14, 23, -21, 8 }, { 11, -41, 95, -162, 195, -111, -247, 5757, 3376, -984, 420, -142, 14, 24, -21, 8 }, { 11, -41, 94, -162, 194, -109, -251, 5755, 3383, -984, 419, -141, 13, 24, -21, 8 }, { 11, -41, 94, -162, 193, -107, -255, 5751, 3390, -984, 419, -141, 13, 24, -21, 8 }, { 11, -41, 94, -161, 192, -105, -258, 5746, 3397, -984, 418, -140, 12, 24, -21, 8 }, { 11, -41, 94, -161, 191, -104, -262, 5743, 3403, -984, 418, -139, 12, 24, -21, 8 }, { 11, -41, 94, -160, 190, -102, -266, 5738, 3410, -984, 417, -139, 12, 25, -21, 8 }, { 11, -41, 94, -160, 189, -100, -270, 5735, 3417, -984, 416, -138, 11, 25, -21, 8 }, { 11, -41, 94, -160, 189, -98, -273, 5729, 3424, -984, 416, -138, 11, 25, -21, 8 }, { 11, -41, 94, -159, 188, -96, -277, 5724, 3431, -984, 415, -137, 11, 25, -21, 8 }, { 11, -41, 94, -159, 187, -94, -281, 5721, 3438, -984, 415, -137, 10, 25, -21, 8 }, { 11, -41, 93, -158, 186, -93, -284, 5716, 3445, -984, 414, -136, 10, 26, -21, 8 }, { 11, -41, 93, -158, 185, -91, -288, 5715, 3451, -984, 413, -135, 9, 26, -22, 8 }, { 11, -41, 93, -158, 184, -89, -292, 5711, 3458, -984, 413, -135, 9, 26, -22, 8 }, { 11, -41, 93, -157, 183, -87, -295, 5705, 3465, -984, 412, -134, 9, 26, -22, 8 }, { 11, -41, 93, -157, 182, -85, -299, 5703, 3472, -984, 411, -134, 8, 26, -22, 8 }, { 11, -41, 93, -156, 181, -83, -302, 5695, 3479, -984, 411, -133, 8, 27, -22, 8 }, { 11, -41, 93, -156, 180, -82, -306, 5693, 3486, -984, 410, -132, 7, 27, -22, 8 }, { 11, -41, 93, -156, 179, -80, -310, 5689, 3493, -983, 409, -132, 7, 27, -22, 8 }, { 11, -41, 93, -155, 178, -78, -313, 5683, 3499, -983, 409, -131, 7, 27, -22, 8 }, { 11, -41, 92, -155, 177, -76, -317, 5682, 3506, -983, 408, -131, 6, 27, -22, 8 }, { 11, -41, 92, -154, 176, -74, -320, 5675, 3513, -983, 408, -130, 6, 27, -22, 8 }, { 11, -41, 92, -154, 175, -72, -324, 5670, 3520, -983, 407, -129, 6, 28, -22, 8 }, { 11, -41, 92, -154, 175, -71, -327, 5667, 3527, -983, 406, -129, 5, 28, -22, 8 }, { 11, -41, 92, -153, 174, -69, -331, 5661, 3534, -983, 406, -128, 5, 28, -22, 8 }, { 11, -41, 92, -153, 173, -67, -334, 5660, 3540, -983, 405, -128, 4, 28, -23, 8 }, { 11, -41, 92, -152, 172, -65, -338, 5654, 3547, -982, 404, -127, 4, 28, -23, 8 }, { 11, -41, 92, -152, 171, -63, -342, 5648, 3554, -982, 404, -126, 4, 29, -23, 8 }, { 11, -41, 92, -152, 170, -62, -345, 5646, 3561, -982, 403, -126, 3, 29, -23, 8 }, { 11, -41, 91, -151, 169, -60, -348, 5641, 3568, -982, 402, -125, 3, 29, -23, 8 }, { 11, -41, 91, -151, 168, -58, -352, 5638, 3575, -982, 401, -125, 2, 29, -23, 9 }, { 11, -41, 91, -150, 167, -56, -355, 5631, 3581, -981, 401, -124, 2, 29, -23, 9 }, { 11, -41, 91, -150, 166, -54, -359, 5626, 3588, -981, 400, -123, 2, 30, -23, 9 }, { 11, -41, 91, -150, 165, -53, -362, 5624, 3595, -981, 399, -123, 1, 30, -23, 9 }, { 11, -41, 91, -149, 164, -51, -366, 5618, 3602, -981, 399, -122, 1, 30, -23, 9 }, { 11, -41, 91, -149, 163, -49, -369, 5613, 3609, -980, 398, -121, 0, 30, -23, 9 }, { 11, -41, 91, -148, 162, -47, -373, 5610, 3615, -980, 397, -121, 0, 30, -23, 9 }, { 11, -41, 90, -148, 161, -45, -376, 5604, 3622, -980, 397, -120, 0, 31, -23, 9 }, { 11, -41, 90, -148, 160, -44, -379, 5603, 3629, -980, 396, -120, -1, 31, -24, 9 }, { 11, -41, 90, -147, 160, -42, -383, 5596, 3636, -979, 395, -119, -1, 31, -24, 9 }, { 11, -41, 90, -147, 159, -40, -386, 5592, 3643, -979, 394, -118, -2, 31, -24, 9 }, { 11, -41, 90, -146, 158, -38, -390, 5588, 3649, -979, 394, -118, -2, 31, -24, 9 }, { 11, -41, 90, -146, 157, -36, -393, 5581, 3656, -978, 393, -117, -2, 32, -24, 9 }, { 11, -41, 90, -146, 156, -35, -396, 5578, 3663, -978, 392, -116, -3, 32, -24, 9 }, { 11, -40, 90, -145, 155, -33, -400, 5573, 3670, -978, 391, -116, -3, 32, -24, 9 }, { 11, -40, 89, -145, 154, -31, -403, 5569, 3677, -978, 391, -115, -4, 32, -24, 9 }, { 11, -40, 89, -144, 153, -29, -406, 5563, 3683, -977, 390, -114, -4, 32, -24, 9 }, { 11, -40, 89, -144, 152, -28, -410, 5560, 3690, -977, 389, -114, -4, 33, -24, 9 }, { 11, -40, 89, -143, 151, -26, -413, 5555, 3697, -977, 388, -113, -5, 33, -24, 9 }, { 11, -40, 89, -143, 150, -24, -416, 5548, 3704, -976, 388, -112, -5, 33, -24, 9 }, { 11, -40, 89, -143, 149, -22, -419, 5546, 3710, -976, 387, -112, -6, 33, -24, 9 }, { 11, -40, 89, -142, 148, -21, -423, 5542, 3717, -975, 386, -111, -6, 33, -25, 9 }, { 11, -40, 88, -142, 147, -19, -426, 5538, 3724, -975, 385, -111, -6, 34, -25, 9 }, { 11, -40, 88, -141, 146, -17, -429, 5532, 3731, -975, 385, -110, -7, 34, -25, 9 }, { 11, -40, 88, -141, 145, -15, -432, 5527, 3737, -974, 384, -109, -7, 34, -25, 9 }, { 11, -40, 88, -141, 145, -13, -436, 5524, 3744, -974, 383, -109, -8, 34, -25, 9 }, { 11, -40, 88, -140, 144, -12, -439, 5518, 3751, -973, 382, -108, -8, 34, -25, 9 }, { 12, -40, 88, -140, 143, -10, -442, 5512, 3758, -973, 381, -107, -9, 35, -25, 9 }, { 12, -40, 88, -139, 142, -8, -445, 5507, 3764, -973, 381, -107, -9, 35, -25, 9 }, { 12, -40, 88, -139, 141, -6, -449, 5502, 3771, -972, 380, -106, -9, 35, -25, 9 }, { 12, -40, 87, -138, 140, -5, -452, 5499, 3778, -972, 379, -105, -10, 35, -25, 9 }, { 12, -40, 87, -138, 139, -3, -455, 5494, 3785, -971, 378, -105, -10, 35, -25, 9 }, { 12, -40, 87, -138, 138, -1, -458, 5490, 3791, -971, 377, -104, -11, 36, -25, 9 }, { 12, -40, 87, -137, 137, 0, -461, 5483, 3798, -970, 377, -103, -11, 36, -25, 9 }, { 12, -40, 87, -137, 136, 2, -464, 5480, 3805, -970, 376, -103, -11, 36, -26, 9 }, { 12, -40, 87, -136, 135, 4, -467, 5474, 3812, -969, 375, -102, -12, 36, -26, 9 }, { 12, -40, 87, -136, 134, 6, -471, 5471, 3818, -969, 374, -101, -12, 36, -26, 9 }, { 12, -40, 86, -135, 133, 7, -474, 5467, 3825, -968, 373, -101, -13, 37, -26, 9 }, { 12, -40, 86, -135, 132, 9, -477, 5462, 3832, -968, 372, -100, -13, 37, -26, 9 }, { 12, -40, 86, -135, 131, 11, -480, 5457, 3838, -967, 372, -99, -14, 37, -26, 9 }, { 12, -40, 86, -134, 130, 13, -483, 5452, 3845, -967, 371, -99, -14, 37, -26, 9 }, { 12, -40, 86, -134, 130, 14, -486, 5446, 3852, -966, 370, -98, -14, 37, -26, 9 }, { 12, -40, 86, -133, 129, 16, -489, 5442, 3858, -966, 369, -97, -15, 37, -26, 9 }, { 12, -40, 86, -133, 128, 18, -492, 5435, 3865, -965, 368, -96, -15, 38, -26, 9 }, { 12, -40, 85, -133, 127, 19, -495, 5434, 3872, -965, 367, -96, -16, 38, -26, 9 }, { 12, -40, 85, -132, 126, 21, -498, 5426, 3879, -964, 367, -95, -16, 38, -26, 9 }, { 12, -40, 85, -132, 125, 23, -501, 5421, 3885, -963, 366, -94, -16, 38, -26, 9 }, { 12, -40, 85, -131, 124, 25, -504, 5417, 3892, -963, 365, -94, -17, 38, -26, 9 }, { 12, -40, 85, -131, 123, 26, -507, 5412, 3899, -962, 364, -93, -17, 39, -27, 9 }, { 12, -40, 85, -130, 122, 28, -510, 5408, 3905, -962, 363, -92, -18, 39, -27, 9 }, { 12, -40, 85, -130, 121, 30, -513, 5403, 3912, -961, 362, -92, -18, 39, -27, 9 }, { 12, -40, 84, -129, 120, 31, -516, 5399, 3919, -960, 361, -91, -19, 39, -27, 9 }, { 12, -40, 84, -129, 119, 33, -519, 5395, 3925, -960, 360, -90, -19, 39, -27, 9 }, { 12, -40, 84, -129, 118, 35, -522, 5387, 3932, -959, 360, -90, -19, 40, -27, 10 }, { 12, -40, 84, -128, 117, 36, -525, 5383, 3938, -958, 359, -89, -20, 40, -27, 10 }, { 12, -40, 84, -128, 116, 38, -528, 5378, 3945, -958, 358, -88, -20, 40, -27, 10 }, { 12, -40, 84, -127, 116, 40, -531, 5371, 3952, -957, 357, -87, -21, 40, -27, 10 }, { 12, -40, 84, -127, 115, 41, -534, 5368, 3958, -956, 356, -87, -21, 40, -27, 10 }, { 12, -40, 83, -126, 114, 43, -537, 5363, 3965, -956, 355, -86, -22, 41, -27, 10 }, { 12, -40, 83, -126, 113, 45, -540, 5357, 3972, -955, 354, -85, -22, 41, -27, 10 }, { 12, -40, 83, -126, 112, 47, -543, 5353, 3978, -954, 353, -85, -22, 41, -27, 10 }, { 12, -39, 83, -125, 111, 48, -546, 5348, 3985, -954, 352, -84, -23, 41, -27, 10 }, { 12, -39, 83, -125, 110, 50, -548, 5342, 3992, -953, 351, -83, -23, 41, -28, 10 }, { 12, -39, 83, -124, 109, 52, -551, 5336, 3998, -952, 350, -82, -24, 42, -28, 10 }, { 12, -39, 82, -124, 108, 53, -554, 5333, 4005, -951, 349, -82, -24, 42, -28, 10 }, { 12, -39, 82, -123, 107, 55, -557, 5327, 4011, -951, 349, -81, -24, 42, -28, 10 }, { 12, -39, 82, -123, 106, 57, -560, 5322, 4018, -950, 348, -80, -25, 42, -28, 10 }, { 12, -39, 82, -123, 105, 58, -563, 5318, 4025, -949, 347, -80, -25, 42, -28, 10 }, { 12, -39, 82, -122, 104, 60, -565, 5311, 4031, -948, 346, -79, -26, 43, -28, 10 }, { 12, -39, 82, -122, 103, 61, -568, 5307, 4038, -948, 345, -78, -26, 43, -28, 10 }, { 12, -39, 82, -121, 102, 63, -571, 5302, 4044, -947, 344, -77, -27, 43, -28, 10 }, { 12, -39, 81, -121, 102, 65, -574, 5297, 4051, -946, 343, -77, -27, 43, -28, 10 }, { 12, -39, 81, -120, 101, 66, -577, 5292, 4058, -945, 342, -76, -28, 43, -28, 10 }, { 12, -39, 81, -120, 100, 68, -579, 5285, 4064, -944, 341, -75, -28, 44, -28, 10 }, { 12, -39, 81, -119, 99, 70, -582, 5279, 4071, -944, 340, -74, -28, 44, -28, 10 }, { 12, -39, 81, -119, 98, 71, -585, 5277, 4077, -943, 339, -74, -29, 44, -28, 10 }, { 12, -39, 81, -119, 97, 73, -588, 5272, 4084, -942, 338, -73, -29, 44, -29, 10 }, { 12, -39, 80, -118, 96, 75, -590, 5267, 4090, -941, 337, -72, -30, 44, -29, 10 }, { 12, -39, 80, -118, 95, 76, -593, 5261, 4097, -940, 336, -71, -30, 45, -29, 10 }, { 12, -39, 80, -117, 94, 78, -596, 5257, 4103, -939, 335, -71, -31, 45, -29, 10 }, { 12, -39, 80, -117, 93, 79, -598, 5251, 4110, -938, 334, -70, -31, 45, -29, 10 }, { 12, -39, 80, -116, 92, 81, -601, 5246, 4116, -938, 333, -69, -31, 45, -29, 10 }, { 12, -39, 80, -116, 91, 83, -604, 5242, 4123, -937, 332, -69, -32, 45, -29, 10 }, { 12, -39, 80, -116, 90, 84, -606, 5235, 4130, -936, 331, -68, -32, 46, -29, 10 }, { 12, -39, 79, -115, 90, 86, -609, 5230, 4136, -935, 330, -67, -33, 46, -29, 10 }, { 12, -39, 79, -115, 89, 88, -612, 5224, 4143, -934, 329, -66, -33, 46, -29, 10 }, { 12, -39, 79, -114, 88, 89, -614, 5220, 4149, -933, 328, -66, -34, 46, -29, 10 }, { 12, -39, 79, -114, 87, 91, -617, 5214, 4156, -932, 327, -65, -34, 46, -29, 10 }, { 12, -39, 79, -113, 86, 92, -620, 5208, 4162, -931, 326, -64, -34, 47, -29, 10 }, { 12, -39, 79, -113, 85, 94, -622, 5202, 4169, -930, 325, -63, -35, 47, -29, 10 }, { 12, -39, 78, -112, 84, 96, -625, 5198, 4175, -929, 324, -63, -35, 47, -29, 10 }, { 12, -39, 78, -112, 83, 97, -627, 5194, 4182, -928, 323, -62, -36, 47, -30, 10 }, { 12, -39, 78, -112, 82, 99, -630, 5189, 4188, -927, 322, -61, -36, 47, -30, 10 }, { 12, -38, 78, -111, 81, 100, -633, 5182, 4195, -926, 321, -60, -37, 48, -30, 10 }, { 12, -38, 78, -111, 80, 102, -635, 5176, 4201, -925, 320, -59, -37, 48, -30, 10 }, { 12, -38, 78, -110, 79, 104, -638, 5171, 4207, -924, 319, -59, -37, 48, -30, 10 }, { 12, -38, 77, -110, 78, 105, -640, 5167, 4214, -923, 318, -58, -38, 48, -30, 10 }, { 12, -38, 77, -109, 78, 107, -643, 5160, 4220, -922, 317, -57, -38, 48, -30, 10 }, { 12, -38, 77, -109, 77, 108, -645, 5154, 4227, -921, 316, -56, -39, 49, -30, 10 }, { 12, -38, 77, -108, 76, 110, -648, 5150, 4233, -920, 314, -56, -39, 49, -30, 10 }, { 12, -38, 77, -108, 75, 111, -650, 5145, 4240, -919, 313, -55, -40, 49, -30, 10 }, { 12, -38, 77, -108, 74, 113, -653, 5140, 4246, -918, 312, -54, -40, 49, -30, 10 }, { 12, -38, 76, -107, 73, 115, -655, 5134, 4253, -917, 311, -53, -41, 49, -30, 10 }, { 12, -38, 76, -107, 72, 116, -658, 5131, 4259, -916, 310, -53, -41, 49, -30, 10 }, { 12, -38, 76, -106, 71, 118, -660, 5123, 4265, -915, 309, -52, -41, 50, -30, 10 }, { 12, -38, 76, -106, 70, 119, -663, 5119, 4272, -914, 308, -51, -42, 50, -30, 10 }, { 12, -38, 76, -105, 69, 121, -665, 5113, 4278, -913, 307, -50, -42, 50, -31, 10 }, { 12, -38, 76, -105, 68, 122, -668, 5108, 4285, -911, 306, -49, -43, 50, -31, 10 }, { 12, -38, 75, -104, 67, 124, -670, 5103, 4291, -910, 305, -49, -43, 50, -31, 10 }, { 12, -38, 75, -104, 67, 125, -672, 5096, 4298, -909, 304, -48, -44, 51, -31, 10 }, { 12, -38, 75, -104, 66, 127, -675, 5091, 4304, -908, 303, -47, -44, 51, -31, 10 }, { 12, -38, 75, -103, 65, 128, -677, 5087, 4310, -907, 301, -46, -45, 51, -31, 10 }, { 12, -38, 75, -103, 64, 130, -680, 5082, 4317, -906, 300, -46, -45, 51, -31, 10 }, { 12, -38, 75, -102, 63, 132, -682, 5075, 4323, -905, 299, -45, -45, 51, -31, 10 }, { 12, -38, 74, -102, 62, 133, -684, 5070, 4329, -903, 298, -44, -46, 52, -31, 10 }, { 12, -38, 74, -101, 61, 135, -687, 5063, 4336, -902, 297, -43, -46, 52, -31, 10 }, { 12, -38, 74, -101, 60, 136, -689, 5059, 4342, -901, 296, -42, -47, 52, -31, 10 }, { 12, -38, 74, -100, 59, 138, -691, 5052, 4349, -900, 295, -42, -47, 52, -31, 10 }, { 12, -38, 74, -100, 58, 139, -694, 5048, 4355, -899, 294, -41, -48, 52, -31, 11 }, { 12, -37, 74, -99, 57, 141, -696, 5039, 4361, -897, 292, -40, -48, 53, -31, 11 }, { 12, -37, 73, -99, 57, 142, -698, 5034, 4368, -896, 291, -39, -49, 53, -31, 11 }, { 12, -37, 73, -99, 56, 144, -701, 5030, 4374, -895, 290, -38, -49, 53, -32, 11 }, { 11, -37, 73, -98, 55, 145, -703, 5026, 4380, -894, 289, -38, -49, 53, -32, 11 }, { 11, -37, 73, -98, 54, 147, -705, 5019, 4387, -892, 288, -37, -50, 53, -32, 11 }, { 11, -37, 73, -97, 53, 148, -707, 5012, 4393, -891, 287, -36, -50, 54, -32, 11 }, { 11, -37, 73, -97, 52, 150, -710, 5008, 4399, -890, 286, -35, -51, 54, -32, 11 }, { 11, -37, 72, -96, 51, 151, -712, 5003, 4405, -888, 284, -34, -51, 54, -32, 11 }, { 11, -37, 72, -96, 50, 153, -714, 4998, 4412, -887, 283, -34, -52, 54, -32, 11 }, { 11, -37, 72, -95, 49, 154, -716, 4992, 4418, -886, 282, -33, -52, 54, -32, 11 }, { 11, -37, 72, -95, 48, 156, -719, 4987, 4424, -885, 281, -32, -53, 55, -32, 11 }, { 11, -37, 72, -95, 48, 157, -721, 4979, 4431, -883, 280, -31, -53, 55, -32, 11 }, { 11, -37, 72, -94, 47, 159, -723, 4972, 4437, -882, 279, -30, -53, 55, -32, 11 }, { 11, -37, 71, -94, 46, 160, -725, 4970, 4443, -880, 277, -30, -54, 55, -32, 11 }, { 11, -37, 71, -93, 45, 162, -727, 4963, 4449, -879, 276, -29, -54, 55, -32, 11 }, { 11, -37, 71, -93, 44, 163, -730, 4958, 4456, -878, 275, -28, -55, 56, -32, 11 }, { 11, -37, 71, -92, 43, 165, -732, 4950, 4462, -876, 274, -27, -55, 56, -32, 11 }, { 11, -37, 71, -92, 42, 166, -734, 4946, 4468, -875, 273, -26, -56, 56, -32, 11 }, { 11, -37, 70, -91, 41, 167, -736, 4943, 4474, -874, 271, -26, -56, 56, -32, 11 }, { 11, -37, 70, -91, 40, 169, -738, 4937, 4481, -872, 270, -25, -57, 56, -33, 11 }, { 11, -37, 70, -90, 40, 170, -740, 4930, 4487, -871, 269, -24, -57, 56, -33, 11 }, { 11, -37, 70, -90, 39, 172, -742, 4922, 4493, -869, 268, -23, -57, 57, -33, 11 }, { 11, -36, 70, -90, 38, 173, -744, 4917, 4499, -868, 267, -22, -58, 57, -33, 11 }, { 11, -36, 70, -89, 37, 175, -747, 4911, 4506, -867, 265, -21, -58, 57, -33, 11 }, { 11, -36, 69, -89, 36, 176, -749, 4908, 4512, -865, 264, -21, -59, 57, -33, 11 }, { 11, -36, 69, -88, 35, 178, -751, 4901, 4518, -864, 263, -20, -59, 57, -33, 11 }, { 11, -36, 69, -88, 34, 179, -753, 4895, 4524, -862, 262, -19, -60, 58, -33, 11 }, { 11, -36, 69, -87, 33, 180, -755, 4890, 4530, -861, 260, -18, -60, 58, -33, 11 }, { 11, -36, 69, -87, 32, 182, -757, 4884, 4536, -859, 259, -17, -61, 58, -33, 11 }, { 11, -36, 69, -86, 32, 183, -759, 4876, 4543, -858, 258, -16, -61, 58, -33, 11 }, { 11, -36, 68, -86, 31, 185, -761, 4872, 4549, -856, 257, -16, -62, 58, -33, 11 }, { 11, -36, 68, -85, 30, 186, -763, 4865, 4555, -855, 256, -15, -62, 59, -33, 11 }, { 11, -36, 68, -85, 29, 188, -765, 4859, 4561, -853, 254, -14, -62, 59, -33, 11 }, { 11, -36, 68, -85, 28, 189, -767, 4855, 4567, -852, 253, -13, -63, 59, -33, 11 }, { 11, -36, 68, -84, 27, 190, -769, 4848, 4573, -850, 252, -12, -63, 59, -33, 11 }, { 11, -36, 67, -84, 26, 192, -771, 4845, 4580, -849, 250, -11, -64, 59, -34, 11 }, { 11, -36, 67, -83, 25, 193, -773, 4838, 4586, -847, 249, -11, -64, 60, -34, 11 }, { 11, -36, 67, -83, 25, 195, -775, 4831, 4592, -845, 248, -10, -65, 60, -34, 11 }, { 11, -36, 67, -82, 24, 196, -777, 4825, 4598, -844, 247, -9, -65, 60, -34, 11 }, { 11, -36, 67, -82, 23, 197, -779, 4821, 4604, -842, 245, -8, -66, 60, -34, 11 }, { 11, -36, 67, -81, 22, 199, -781, 4814, 4610, -841, 244, -7, -66, 60, -34, 11 }, { 11, -36, 66, -81, 21, 200, -783, 4809, 4616, -839, 243, -6, -66, 60, -34, 11 }, { 11, -36, 66, -81, 20, 202, -785, 4803, 4622, -837, 242, -6, -67, 61, -34, 11 }, { 11, -35, 66, -80, 19, 203, -786, 4796, 4628, -836, 240, -5, -67, 61, -34, 11 }, { 11, -35, 66, -80, 18, 204, -788, 4791, 4634, -834, 239, -4, -68, 61, -34, 11 }, { 11, -35, 66, -79, 18, 206, -790, 4782, 4641, -833, 238, -3, -68, 61, -34, 11 }, { 11, -35, 65, -79, 17, 207, -792, 4779, 4647, -831, 236, -2, -69, 61, -34, 11 }, { 11, -35, 65, -78, 16, 208, -794, 4771, 4653, -829, 235, -1, -69, 62, -34, 11 }, { 11, -35, 65, -78, 15, 210, -796, 4767, 4659, -828, 234, -1, -70, 62, -34, 11 }, { 11, -35, 65, -77, 14, 211, -798, 4760, 4665, -826, 233, 0, -70, 62, -34, 11 }, { 11, -35, 65, -77, 13, 213, -799, 4754, 4671, -824, 231, 1, -71, 62, -34, 11 }, { 11, -35, 65, -76, 12, 214, -801, 4747, 4677, -822, 230, 2, -71, 62, -34, 11 }, { 11, -35, 64, -76, 11, 215, -803, 4742, 4683, -821, 229, 3, -71, 63, -34, 11 }, { 11, -35, 64, -76, 11, 217, -805, 4737, 4689, -819, 227, 4, -72, 63, -35, 11 }, { 11, -35, 64, -75, 10, 218, -807, 4730, 4695, -817, 226, 5, -72, 63, -35, 11 }, { 11, -35, 64, -75, 9, 219, -808, 4726, 4701, -816, 225, 5, -73, 63, -35, 11 }, { 11, -35, 64, -74, 8, 221, -810, 4719, 4707, -814, 223, 6, -73, 63, -35, 11 }, { 11, -35, 63, -74, 7, 222, -812, 4714, 4714, -812, 222, 7, -74, 63, -35, 11 }, { 11, -35, 63, -73, 6, 223, -814, 4707, 4719, -810, 221, 8, -74, 64, -35, 11 }, { 11, -35, 63, -73, 5, 225, -816, 4701, 4726, -808, 219, 9, -75, 64, -35, 11 }, { 11, -35, 63, -72, 5, 226, -817, 4695, 4730, -807, 218, 10, -75, 64, -35, 11 }, { 11, -35, 63, -72, 4, 227, -819, 4689, 4737, -805, 217, 11, -76, 64, -35, 11 }, { 11, -34, 63, -71, 3, 229, -821, 4683, 4742, -803, 215, 11, -76, 64, -35, 11 }, { 11, -34, 62, -71, 2, 230, -822, 4677, 4747, -801, 214, 12, -76, 65, -35, 11 }, { 11, -34, 62, -71, 1, 231, -824, 4671, 4754, -799, 213, 13, -77, 65, -35, 11 }, { 11, -34, 62, -70, 0, 233, -826, 4665, 4760, -798, 211, 14, -77, 65, -35, 11 }, { 11, -34, 62, -70, -1, 234, -828, 4659, 4767, -796, 210, 15, -78, 65, -35, 11 }, { 11, -34, 62, -69, -1, 235, -829, 4653, 4771, -794, 208, 16, -78, 65, -35, 11 }, { 11, -34, 61, -69, -2, 236, -831, 4647, 4779, -792, 207, 17, -79, 65, -35, 11 }, { 11, -34, 61, -68, -3, 238, -833, 4641, 4782, -790, 206, 18, -79, 66, -35, 11 }, { 11, -34, 61, -68, -4, 239, -834, 4634, 4791, -788, 204, 18, -80, 66, -35, 11 }, { 11, -34, 61, -67, -5, 240, -836, 4628, 4796, -786, 203, 19, -80, 66, -35, 11 }, { 11, -34, 61, -67, -6, 242, -837, 4622, 4803, -785, 202, 20, -81, 66, -36, 11 }, { 11, -34, 60, -66, -6, 243, -839, 4616, 4809, -783, 200, 21, -81, 66, -36, 11 }, { 11, -34, 60, -66, -7, 244, -841, 4610, 4814, -781, 199, 22, -81, 67, -36, 11 }, { 11, -34, 60, -66, -8, 245, -842, 4604, 4821, -779, 197, 23, -82, 67, -36, 11 }, { 11, -34, 60, -65, -9, 247, -844, 4598, 4825, -777, 196, 24, -82, 67, -36, 11 }, { 11, -34, 60, -65, -10, 248, -845, 4592, 4831, -775, 195, 25, -83, 67, -36, 11 }, { 11, -34, 60, -64, -11, 249, -847, 4586, 4838, -773, 193, 25, -83, 67, -36, 11 }, { 11, -34, 59, -64, -11, 250, -849, 4580, 4845, -771, 192, 26, -84, 67, -36, 11 }, { 11, -33, 59, -63, -12, 252, -850, 4573, 4848, -769, 190, 27, -84, 68, -36, 11 }, { 11, -33, 59, -63, -13, 253, -852, 4567, 4855, -767, 189, 28, -85, 68, -36, 11 }, { 11, -33, 59, -62, -14, 254, -853, 4561, 4859, -765, 188, 29, -85, 68, -36, 11 }, { 11, -33, 59, -62, -15, 256, -855, 4555, 4865, -763, 186, 30, -85, 68, -36, 11 }, { 11, -33, 58, -62, -16, 257, -856, 4549, 4872, -761, 185, 31, -86, 68, -36, 11 }, { 11, -33, 58, -61, -16, 258, -858, 4543, 4876, -759, 183, 32, -86, 69, -36, 11 }, { 11, -33, 58, -61, -17, 259, -859, 4536, 4884, -757, 182, 32, -87, 69, -36, 11 }, { 11, -33, 58, -60, -18, 260, -861, 4530, 4890, -755, 180, 33, -87, 69, -36, 11 }, { 11, -33, 58, -60, -19, 262, -862, 4524, 4895, -753, 179, 34, -88, 69, -36, 11 }, { 11, -33, 57, -59, -20, 263, -864, 4518, 4901, -751, 178, 35, -88, 69, -36, 11 }, { 11, -33, 57, -59, -21, 264, -865, 4512, 4908, -749, 176, 36, -89, 69, -36, 11 }, { 11, -33, 57, -58, -21, 265, -867, 4506, 4911, -747, 175, 37, -89, 70, -36, 11 }, { 11, -33, 57, -58, -22, 267, -868, 4499, 4917, -744, 173, 38, -90, 70, -36, 11 }, { 11, -33, 57, -57, -23, 268, -869, 4493, 4922, -742, 172, 39, -90, 70, -37, 11 }, { 11, -33, 56, -57, -24, 269, -871, 4487, 4930, -740, 170, 40, -90, 70, -37, 11 }, { 11, -33, 56, -57, -25, 270, -872, 4481, 4937, -738, 169, 40, -91, 70, -37, 11 }, { 11, -32, 56, -56, -26, 271, -874, 4474, 4943, -736, 167, 41, -91, 70, -37, 11 }, { 11, -32, 56, -56, -26, 273, -875, 4468, 4946, -734, 166, 42, -92, 71, -37, 11 }, { 11, -32, 56, -55, -27, 274, -876, 4462, 4950, -732, 165, 43, -92, 71, -37, 11 }, { 11, -32, 56, -55, -28, 275, -878, 4456, 4958, -730, 163, 44, -93, 71, -37, 11 }, { 11, -32, 55, -54, -29, 276, -879, 4449, 4963, -727, 162, 45, -93, 71, -37, 11 }, { 11, -32, 55, -54, -30, 277, -880, 4443, 4970, -725, 160, 46, -94, 71, -37, 11 }, { 11, -32, 55, -53, -30, 279, -882, 4437, 4972, -723, 159, 47, -94, 72, -37, 11 }, { 11, -32, 55, -53, -31, 280, -883, 4431, 4979, -721, 157, 48, -95, 72, -37, 11 }, { 11, -32, 55, -53, -32, 281, -885, 4424, 4987, -719, 156, 48, -95, 72, -37, 11 }, { 11, -32, 54, -52, -33, 282, -886, 4418, 4992, -716, 154, 49, -95, 72, -37, 11 }, { 11, -32, 54, -52, -34, 283, -887, 4412, 4998, -714, 153, 50, -96, 72, -37, 11 }, { 11, -32, 54, -51, -34, 284, -888, 4405, 5003, -712, 151, 51, -96, 72, -37, 11 }, { 11, -32, 54, -51, -35, 286, -890, 4399, 5008, -710, 150, 52, -97, 73, -37, 11 }, { 11, -32, 54, -50, -36, 287, -891, 4393, 5012, -707, 148, 53, -97, 73, -37, 11 }, { 11, -32, 53, -50, -37, 288, -892, 4387, 5019, -705, 147, 54, -98, 73, -37, 11 }, { 11, -32, 53, -49, -38, 289, -894, 4380, 5026, -703, 145, 55, -98, 73, -37, 11 }, { 11, -32, 53, -49, -38, 290, -895, 4374, 5030, -701, 144, 56, -99, 73, -37, 12 }, { 11, -31, 53, -49, -39, 291, -896, 4368, 5034, -698, 142, 57, -99, 73, -37, 12 }, { 11, -31, 53, -48, -40, 292, -897, 4361, 5039, -696, 141, 57, -99, 74, -37, 12 }, { 11, -31, 52, -48, -41, 294, -899, 4355, 5048, -694, 139, 58, -100, 74, -38, 12 }, { 10, -31, 52, -47, -42, 295, -900, 4349, 5052, -691, 138, 59, -100, 74, -38, 12 }, { 10, -31, 52, -47, -42, 296, -901, 4342, 5059, -689, 136, 60, -101, 74, -38, 12 }, { 10, -31, 52, -46, -43, 297, -902, 4336, 5063, -687, 135, 61, -101, 74, -38, 12 }, { 10, -31, 52, -46, -44, 298, -903, 4329, 5070, -684, 133, 62, -102, 74, -38, 12 }, { 10, -31, 51, -45, -45, 299, -905, 4323, 5075, -682, 132, 63, -102, 75, -38, 12 }, { 10, -31, 51, -45, -46, 300, -906, 4317, 5082, -680, 130, 64, -103, 75, -38, 12 }, { 10, -31, 51, -45, -46, 301, -907, 4310, 5087, -677, 128, 65, -103, 75, -38, 12 }, { 10, -31, 51, -44, -47, 303, -908, 4304, 5091, -675, 127, 66, -104, 75, -38, 12 }, { 10, -31, 51, -44, -48, 304, -909, 4298, 5096, -672, 125, 67, -104, 75, -38, 12 }, { 10, -31, 50, -43, -49, 305, -910, 4291, 5103, -670, 124, 67, -104, 75, -38, 12 }, { 10, -31, 50, -43, -49, 306, -911, 4285, 5108, -668, 122, 68, -105, 76, -38, 12 }, { 10, -31, 50, -42, -50, 307, -913, 4278, 5113, -665, 121, 69, -105, 76, -38, 12 }, { 10, -30, 50, -42, -51, 308, -914, 4272, 5119, -663, 119, 70, -106, 76, -38, 12 }, { 10, -30, 50, -41, -52, 309, -915, 4265, 5123, -660, 118, 71, -106, 76, -38, 12 }, { 10, -30, 49, -41, -53, 310, -916, 4259, 5131, -658, 116, 72, -107, 76, -38, 12 }, { 10, -30, 49, -41, -53, 311, -917, 4253, 5134, -655, 115, 73, -107, 76, -38, 12 }, { 10, -30, 49, -40, -54, 312, -918, 4246, 5140, -653, 113, 74, -108, 77, -38, 12 }, { 10, -30, 49, -40, -55, 313, -919, 4240, 5145, -650, 111, 75, -108, 77, -38, 12 }, { 10, -30, 49, -39, -56, 314, -920, 4233, 5150, -648, 110, 76, -108, 77, -38, 12 }, { 10, -30, 49, -39, -56, 316, -921, 4227, 5154, -645, 108, 77, -109, 77, -38, 12 }, { 10, -30, 48, -38, -57, 317, -922, 4220, 5160, -643, 107, 78, -109, 77, -38, 12 }, { 10, -30, 48, -38, -58, 318, -923, 4214, 5167, -640, 105, 78, -110, 77, -38, 12 }, { 10, -30, 48, -37, -59, 319, -924, 4207, 5171, -638, 104, 79, -110, 78, -38, 12 }, { 10, -30, 48, -37, -59, 320, -925, 4201, 5176, -635, 102, 80, -111, 78, -38, 12 }, { 10, -30, 48, -37, -60, 321, -926, 4195, 5182, -633, 100, 81, -111, 78, -38, 12 }, { 10, -30, 47, -36, -61, 322, -927, 4188, 5189, -630, 99, 82, -112, 78, -39, 12 }, { 10, -30, 47, -36, -62, 323, -928, 4182, 5194, -627, 97, 83, -112, 78, -39, 12 }, { 10, -29, 47, -35, -63, 324, -929, 4175, 5198, -625, 96, 84, -112, 78, -39, 12 }, { 10, -29, 47, -35, -63, 325, -930, 4169, 5202, -622, 94, 85, -113, 79, -39, 12 }, { 10, -29, 47, -34, -64, 326, -931, 4162, 5208, -620, 92, 86, -113, 79, -39, 12 }, { 10, -29, 46, -34, -65, 327, -932, 4156, 5214, -617, 91, 87, -114, 79, -39, 12 }, { 10, -29, 46, -34, -66, 328, -933, 4149, 5220, -614, 89, 88, -114, 79, -39, 12 }, { 10, -29, 46, -33, -66, 329, -934, 4143, 5224, -612, 88, 89, -115, 79, -39, 12 }, { 10, -29, 46, -33, -67, 330, -935, 4136, 5230, -609, 86, 90, -115, 79, -39, 12 }, { 10, -29, 46, -32, -68, 331, -936, 4130, 5235, -606, 84, 90, -116, 80, -39, 12 }, { 10, -29, 45, -32, -69, 332, -937, 4123, 5242, -604, 83, 91, -116, 80, -39, 12 }, { 10, -29, 45, -31, -69, 333, -938, 4116, 5246, -601, 81, 92, -116, 80, -39, 12 }, { 10, -29, 45, -31, -70, 334, -938, 4110, 5251, -598, 79, 93, -117, 80, -39, 12 }, { 10, -29, 45, -31, -71, 335, -939, 4103, 5257, -596, 78, 94, -117, 80, -39, 12 }, { 10, -29, 45, -30, -71, 336, -940, 4097, 5261, -593, 76, 95, -118, 80, -39, 12 }, { 10, -29, 44, -30, -72, 337, -941, 4090, 5267, -590, 75, 96, -118, 80, -39, 12 }, { 10, -29, 44, -29, -73, 338, -942, 4084, 5272, -588, 73, 97, -119, 81, -39, 12 }, { 10, -28, 44, -29, -74, 339, -943, 4077, 5277, -585, 71, 98, -119, 81, -39, 12 }, { 10, -28, 44, -28, -74, 340, -944, 4071, 5279, -582, 70, 99, -119, 81, -39, 12 }, { 10, -28, 44, -28, -75, 341, -944, 4064, 5285, -579, 68, 100, -120, 81, -39, 12 }, { 10, -28, 43, -28, -76, 342, -945, 4058, 5292, -577, 66, 101, -120, 81, -39, 12 }, { 10, -28, 43, -27, -77, 343, -946, 4051, 5297, -574, 65, 102, -121, 81, -39, 12 }, { 10, -28, 43, -27, -77, 344, -947, 4044, 5302, -571, 63, 102, -121, 82, -39, 12 }, { 10, -28, 43, -26, -78, 345, -948, 4038, 5307, -568, 61, 103, -122, 82, -39, 12 }, { 10, -28, 43, -26, -79, 346, -948, 4031, 5311, -565, 60, 104, -122, 82, -39, 12 }, { 10, -28, 42, -25, -80, 347, -949, 4025, 5318, -563, 58, 105, -123, 82, -39, 12 }, { 10, -28, 42, -25, -80, 348, -950, 4018, 5322, -560, 57, 106, -123, 82, -39, 12 }, { 10, -28, 42, -24, -81, 349, -951, 4011, 5327, -557, 55, 107, -123, 82, -39, 12 }, { 10, -28, 42, -24, -82, 349, -951, 4005, 5333, -554, 53, 108, -124, 82, -39, 12 }, { 10, -28, 42, -24, -82, 350, -952, 3998, 5336, -551, 52, 109, -124, 83, -39, 12 }, { 10, -28, 41, -23, -83, 351, -953, 3992, 5342, -548, 50, 110, -125, 83, -39, 12 }, { 10, -27, 41, -23, -84, 352, -954, 3985, 5348, -546, 48, 111, -125, 83, -39, 12 }, { 10, -27, 41, -22, -85, 353, -954, 3978, 5353, -543, 47, 112, -126, 83, -40, 12 }, { 10, -27, 41, -22, -85, 354, -955, 3972, 5357, -540, 45, 113, -126, 83, -40, 12 }, { 10, -27, 41, -22, -86, 355, -956, 3965, 5363, -537, 43, 114, -126, 83, -40, 12 }, { 10, -27, 40, -21, -87, 356, -956, 3958, 5368, -534, 41, 115, -127, 84, -40, 12 }, { 10, -27, 40, -21, -87, 357, -957, 3952, 5371, -531, 40, 116, -127, 84, -40, 12 }, { 10, -27, 40, -20, -88, 358, -958, 3945, 5378, -528, 38, 116, -128, 84, -40, 12 }, { 10, -27, 40, -20, -89, 359, -958, 3938, 5383, -525, 36, 117, -128, 84, -40, 12 }, { 10, -27, 40, -19, -90, 360, -959, 3932, 5387, -522, 35, 118, -129, 84, -40, 12 }, { 9, -27, 39, -19, -90, 360, -960, 3925, 5395, -519, 33, 119, -129, 84, -40, 12 }, { 9, -27, 39, -19, -91, 361, -960, 3919, 5399, -516, 31, 120, -129, 84, -40, 12 }, { 9, -27, 39, -18, -92, 362, -961, 3912, 5403, -513, 30, 121, -130, 85, -40, 12 }, { 9, -27, 39, -18, -92, 363, -962, 3905, 5408, -510, 28, 122, -130, 85, -40, 12 }, { 9, -27, 39, -17, -93, 364, -962, 3899, 5412, -507, 26, 123, -131, 85, -40, 12 }, { 9, -26, 38, -17, -94, 365, -963, 3892, 5417, -504, 25, 124, -131, 85, -40, 12 }, { 9, -26, 38, -16, -94, 366, -963, 3885, 5421, -501, 23, 125, -132, 85, -40, 12 }, { 9, -26, 38, -16, -95, 367, -964, 3879, 5426, -498, 21, 126, -132, 85, -40, 12 }, { 9, -26, 38, -16, -96, 367, -965, 3872, 5434, -495, 19, 127, -133, 85, -40, 12 }, { 9, -26, 38, -15, -96, 368, -965, 3865, 5435, -492, 18, 128, -133, 86, -40, 12 }, { 9, -26, 37, -15, -97, 369, -966, 3858, 5442, -489, 16, 129, -133, 86, -40, 12 }, { 9, -26, 37, -14, -98, 370, -966, 3852, 5446, -486, 14, 130, -134, 86, -40, 12 }, { 9, -26, 37, -14, -99, 371, -967, 3845, 5452, -483, 13, 130, -134, 86, -40, 12 }, { 9, -26, 37, -14, -99, 372, -967, 3838, 5457, -480, 11, 131, -135, 86, -40, 12 }, { 9, -26, 37, -13, -100, 372, -968, 3832, 5462, -477, 9, 132, -135, 86, -40, 12 }, { 9, -26, 37, -13, -101, 373, -968, 3825, 5467, -474, 7, 133, -135, 86, -40, 12 }, { 9, -26, 36, -12, -101, 374, -969, 3818, 5471, -471, 6, 134, -136, 87, -40, 12 }, { 9, -26, 36, -12, -102, 375, -969, 3812, 5474, -467, 4, 135, -136, 87, -40, 12 }, { 9, -26, 36, -11, -103, 376, -970, 3805, 5480, -464, 2, 136, -137, 87, -40, 12 }, { 9, -25, 36, -11, -103, 377, -970, 3798, 5483, -461, 0, 137, -137, 87, -40, 12 }, { 9, -25, 36, -11, -104, 377, -971, 3791, 5490, -458, -1, 138, -138, 87, -40, 12 }, { 9, -25, 35, -10, -105, 378, -971, 3785, 5494, -455, -3, 139, -138, 87, -40, 12 }, { 9, -25, 35, -10, -105, 379, -972, 3778, 5499, -452, -5, 140, -138, 87, -40, 12 }, { 9, -25, 35, -9, -106, 380, -972, 3771, 5502, -449, -6, 141, -139, 88, -40, 12 }, { 9, -25, 35, -9, -107, 381, -973, 3764, 5507, -445, -8, 142, -139, 88, -40, 12 }, { 9, -25, 35, -9, -107, 381, -973, 3758, 5512, -442, -10, 143, -140, 88, -40, 12 }, { 9, -25, 34, -8, -108, 382, -973, 3751, 5518, -439, -12, 144, -140, 88, -40, 11 }, { 9, -25, 34, -8, -109, 383, -974, 3744, 5524, -436, -13, 145, -141, 88, -40, 11 }, { 9, -25, 34, -7, -109, 384, -974, 3737, 5527, -432, -15, 145, -141, 88, -40, 11 }, { 9, -25, 34, -7, -110, 385, -975, 3731, 5532, -429, -17, 146, -141, 88, -40, 11 }, { 9, -25, 34, -6, -111, 385, -975, 3724, 5538, -426, -19, 147, -142, 88, -40, 11 }, { 9, -25, 33, -6, -111, 386, -975, 3717, 5542, -423, -21, 148, -142, 89, -40, 11 }, { 9, -24, 33, -6, -112, 387, -976, 3710, 5546, -419, -22, 149, -143, 89, -40, 11 }, { 9, -24, 33, -5, -112, 388, -976, 3704, 5548, -416, -24, 150, -143, 89, -40, 11 }, { 9, -24, 33, -5, -113, 388, -977, 3697, 5555, -413, -26, 151, -143, 89, -40, 11 }, { 9, -24, 33, -4, -114, 389, -977, 3690, 5560, -410, -28, 152, -144, 89, -40, 11 }, { 9, -24, 32, -4, -114, 390, -977, 3683, 5563, -406, -29, 153, -144, 89, -40, 11 }, { 9, -24, 32, -4, -115, 391, -978, 3677, 5569, -403, -31, 154, -145, 89, -40, 11 }, { 9, -24, 32, -3, -116, 391, -978, 3670, 5573, -400, -33, 155, -145, 90, -40, 11 }, { 9, -24, 32, -3, -116, 392, -978, 3663, 5578, -396, -35, 156, -146, 90, -41, 11 }, { 9, -24, 32, -2, -117, 393, -978, 3656, 5581, -393, -36, 157, -146, 90, -41, 11 }, { 9, -24, 31, -2, -118, 394, -979, 3649, 5588, -390, -38, 158, -146, 90, -41, 11 }, { 9, -24, 31, -2, -118, 394, -979, 3643, 5592, -386, -40, 159, -147, 90, -41, 11 }, { 9, -24, 31, -1, -119, 395, -979, 3636, 5596, -383, -42, 160, -147, 90, -41, 11 }, { 9, -24, 31, -1, -120, 396, -980, 3629, 5603, -379, -44, 160, -148, 90, -41, 11 }, { 9, -23, 31, 0, -120, 397, -980, 3622, 5604, -376, -45, 161, -148, 90, -41, 11 }, { 9, -23, 30, 0, -121, 397, -980, 3615, 5610, -373, -47, 162, -148, 91, -41, 11 }, { 9, -23, 30, 0, -121, 398, -980, 3609, 5613, -369, -49, 163, -149, 91, -41, 11 }, { 9, -23, 30, 1, -122, 399, -981, 3602, 5618, -366, -51, 164, -149, 91, -41, 11 }, { 9, -23, 30, 1, -123, 399, -981, 3595, 5624, -362, -53, 165, -150, 91, -41, 11 }, { 9, -23, 30, 2, -123, 400, -981, 3588, 5626, -359, -54, 166, -150, 91, -41, 11 }, { 9, -23, 29, 2, -124, 401, -981, 3581, 5631, -355, -56, 167, -150, 91, -41, 11 }, { 9, -23, 29, 2, -125, 401, -982, 3575, 5638, -352, -58, 168, -151, 91, -41, 11 }, { 8, -23, 29, 3, -125, 402, -982, 3568, 5641, -348, -60, 169, -151, 91, -41, 11 }, { 8, -23, 29, 3, -126, 403, -982, 3561, 5646, -345, -62, 170, -152, 92, -41, 11 }, { 8, -23, 29, 4, -126, 404, -982, 3554, 5648, -342, -63, 171, -152, 92, -41, 11 }, { 8, -23, 28, 4, -127, 404, -982, 3547, 5654, -338, -65, 172, -152, 92, -41, 11 }, { 8, -23, 28, 4, -128, 405, -983, 3540, 5660, -334, -67, 173, -153, 92, -41, 11 }, { 8, -22, 28, 5, -128, 406, -983, 3534, 5661, -331, -69, 174, -153, 92, -41, 11 }, { 8, -22, 28, 5, -129, 406, -983, 3527, 5667, -327, -71, 175, -154, 92, -41, 11 }, { 8, -22, 28, 6, -129, 407, -983, 3520, 5670, -324, -72, 175, -154, 92, -41, 11 }, { 8, -22, 27, 6, -130, 408, -983, 3513, 5675, -320, -74, 176, -154, 92, -41, 11 }, { 8, -22, 27, 6, -131, 408, -983, 3506, 5682, -317, -76, 177, -155, 92, -41, 11 }, { 8, -22, 27, 7, -131, 409, -983, 3499, 5683, -313, -78, 178, -155, 93, -41, 11 }, { 8, -22, 27, 7, -132, 409, -983, 3493, 5689, -310, -80, 179, -156, 93, -41, 11 }, { 8, -22, 27, 7, -132, 410, -984, 3486, 5693, -306, -82, 180, -156, 93, -41, 11 }, { 8, -22, 27, 8, -133, 411, -984, 3479, 5695, -302, -83, 181, -156, 93, -41, 11 }, { 8, -22, 26, 8, -134, 411, -984, 3472, 5703, -299, -85, 182, -157, 93, -41, 11 }, { 8, -22, 26, 9, -134, 412, -984, 3465, 5705, -295, -87, 183, -157, 93, -41, 11 }, { 8, -22, 26, 9, -135, 413, -984, 3458, 5711, -292, -89, 184, -158, 93, -41, 11 }, { 8, -22, 26, 9, -135, 413, -984, 3451, 5715, -288, -91, 185, -158, 93, -41, 11 }, { 8, -21, 26, 10, -136, 414, -984, 3445, 5716, -284, -93, 186, -158, 93, -41, 11 }, { 8, -21, 25, 10, -137, 415, -984, 3438, 5721, -281, -94, 187, -159, 94, -41, 11 }, { 8, -21, 25, 11, -137, 415, -984, 3431, 5724, -277, -96, 188, -159, 94, -41, 11 }, { 8, -21, 25, 11, -138, 416, -984, 3424, 5729, -273, -98, 189, -160, 94, -41, 11 }, { 8, -21, 25, 11, -138, 416, -984, 3417, 5735, -270, -100, 189, -160, 94, -41, 11 }, { 8, -21, 25, 12, -139, 417, -984, 3410, 5738, -266, -102, 190, -160, 94, -41, 11 }, { 8, -21, 24, 12, -139, 418, -984, 3403, 5743, -262, -104, 191, -161, 94, -41, 11 }, { 8, -21, 24, 12, -140, 418, -984, 3397, 5746, -258, -105, 192, -161, 94, -41, 11 }, { 8, -21, 24, 13, -141, 419, -984, 3390, 5751, -255, -107, 193, -162, 94, -41, 11 }, { 8, -21, 24, 13, -141, 419, -984, 3383, 5755, -251, -109, 194, -162, 94, -41, 11 }, { 8, -21, 24, 14, -142, 420, -984, 3376, 5757, -247, -111, 195, -162, 95, -41, 11 }, { 8, -21, 23, 14, -142, 420, -984, 3369, 5764, -244, -113, 196, -163, 95, -41, 11 }, { 8, -21, 23, 14, -143, 421, -984, 3362, 5768, -240, -115, 197, -163, 95, -41, 11 }, { 8, -20, 23, 15, -143, 422, -984, 3355, 5769, -236, -117, 198, -163, 95, -41, 11 }, { 8, -20, 23, 15, -144, 422, -984, 3348, 5774, -232, -118, 199, -164, 95, -41, 11 }, { 8, -20, 23, 16, -145, 423, -984, 3342, 5776, -228, -120, 200, -164, 95, -41, 11 }, { 8, -20, 22, 16, -145, 423, -984, 3335, 5783, -225, -122, 201, -165, 95, -41, 11 }, { 8, -20, 22, 16, -146, 424, -984, 3328, 5787, -221, -124, 202, -165, 95, -41, 11 }, { 8, -20, 22, 17, -146, 424, -984, 3321, 5790, -217, -126, 203, -165, 95, -41, 11 }, { 8, -20, 22, 17, -147, 425, -984, 3314, 5795, -213, -128, 203, -166, 96, -41, 11 }, { 8, -20, 22, 17, -147, 425, -984, 3307, 5799, -209, -130, 204, -166, 96, -41, 11 }, { 8, -20, 22, 18, -148, 426, -984, 3300, 5801, -205, -131, 205, -166, 96, -41, 11 }, { 8, -20, 21, 18, -148, 427, -984, 3293, 5807, -202, -133, 206, -167, 96, -41, 11 }, { 8, -20, 21, 19, -149, 427, -983, 3286, 5810, -198, -135, 207, -167, 96, -41, 11 }, { 8, -20, 21, 19, -149, 428, -983, 3280, 5813, -194, -137, 208, -168, 96, -41, 11 }, { 8, -20, 21, 19, -150, 428, -983, 3273, 5818, -190, -139, 209, -168, 96, -41, 11 }, { 8, -19, 21, 20, -151, 429, -983, 3266, 5820, -186, -141, 210, -168, 96, -41, 11 }, { 8, -19, 20, 20, -151, 429, -983, 3259, 5826, -182, -143, 211, -169, 96, -41, 11 }, { 8, -19, 20, 20, -152, 430, -983, 3252, 5830, -178, -145, 212, -169, 96, -41, 11 }, { 7, -19, 20, 21, -152, 430, -983, 3245, 5833, -174, -146, 213, -169, 96, -41, 11 }, { 7, -19, 20, 21, -153, 431, -982, 3238, 5836, -170, -148, 214, -170, 97, -41, 11 }, { 7, -19, 20, 21, -153, 431, -982, 3231, 5840, -166, -150, 215, -170, 97, -41, 11 }, { 7, -19, 19, 22, -154, 432, -982, 3224, 5847, -163, -152, 215, -171, 97, -41, 11 }, { 7, -19, 19, 22, -154, 432, -982, 3217, 5851, -159, -154, 216, -171, 97, -41, 11 }, { 7, -19, 19, 23, -155, 433, -982, 3210, 5854, -155, -156, 217, -171, 97, -41, 11 }, { 7, -19, 19, 23, -155, 433, -981, 3204, 5857, -151, -158, 218, -172, 97, -41, 11 }, { 7, -19, 19, 23, -156, 434, -981, 3197, 5861, -147, -160, 219, -172, 97, -41, 11 }, { 7, -19, 18, 24, -156, 434, -981, 3190, 5865, -143, -162, 220, -172, 97, -41, 11 }, { 7, -18, 18, 24, -157, 435, -981, 3183, 5868, -139, -163, 221, -173, 97, -41, 11 }, { 7, -18, 18, 24, -157, 435, -981, 3176, 5872, -135, -165, 222, -173, 97, -41, 11 }, { 7, -18, 18, 25, -158, 436, -980, 3169, 5873, -131, -167, 223, -173, 98, -41, 11 }, { 7, -18, 18, 25, -158, 436, -980, 3162, 5878, -127, -169, 224, -174, 98, -41, 11 }, { 7, -18, 18, 25, -159, 436, -980, 3155, 5882, -122, -171, 225, -174, 98, -41, 11 }, { 7, -18, 17, 26, -159, 437, -980, 3148, 5886, -118, -173, 226, -175, 98, -41, 11 }, { 7, -18, 17, 26, -160, 437, -979, 3141, 5891, -114, -175, 226, -175, 98, -41, 11 }, { 7, -18, 17, 27, -161, 438, -979, 3134, 5894, -110, -177, 227, -175, 98, -41, 11 }, { 7, -18, 17, 27, -161, 438, -979, 3127, 5899, -106, -179, 228, -176, 98, -41, 11 }, { 7, -18, 17, 27, -162, 439, -978, 3121, 5900, -102, -180, 229, -176, 98, -41, 11 }, { 7, -18, 16, 28, -162, 439, -978, 3114, 5904, -98, -182, 230, -176, 98, -41, 11 }, { 7, -18, 16, 28, -163, 440, -978, 3107, 5909, -94, -184, 231, -177, 98, -41, 11 }, { 7, -18, 16, 28, -163, 440, -978, 3100, 5913, -90, -186, 232, -177, 98, -41, 11 }, { 7, -17, 16, 29, -164, 440, -977, 3093, 5915, -86, -188, 233, -177, 98, -41, 11 }, { 7, -17, 16, 29, -164, 441, -977, 3086, 5918, -82, -190, 234, -178, 99, -41, 11 }, { 7, -17, 15, 29, -164, 441, -977, 3079, 5922, -77, -192, 235, -178, 99, -41, 11 }, { 7, -17, 15, 30, -165, 442, -976, 3072, 5924, -73, -194, 236, -178, 99, -41, 11 }, { 7, -17, 15, 30, -165, 442, -976, 3065, 5930, -69, -196, 236, -179, 99, -41, 11 }, { 7, -17, 15, 30, -166, 442, -976, 3058, 5935, -65, -198, 237, -179, 99, -41, 11 }, { 7, -17, 15, 31, -166, 443, -975, 3051, 5937, -61, -200, 238, -180, 99, -41, 11 }, { 7, -17, 15, 31, -167, 443, -975, 3044, 5941, -56, -201, 239, -180, 99, -41, 10 }, { 7, -17, 14, 31, -167, 444, -974, 3037, 5944, -52, -203, 240, -180, 99, -41, 10 }, { 7, -17, 14, 32, -168, 444, -974, 3031, 5947, -48, -205, 241, -181, 99, -40, 10 }, { 7, -17, 14, 32, -168, 444, -974, 3024, 5951, -44, -207, 242, -181, 99, -40, 10 }, { 7, -17, 14, 32, -169, 445, -973, 3017, 5954, -40, -209, 243, -181, 99, -40, 10 }, { 7, -16, 14, 33, -169, 445, -973, 3010, 5956, -35, -211, 244, -182, 99, -40, 10 }, { 7, -16, 13, 33, -170, 446, -973, 3003, 5961, -31, -213, 245, -182, 99, -40, 10 }, { 7, -16, 13, 34, -170, 446, -972, 2996, 5963, -27, -215, 245, -182, 100, -40, 10 }, { 7, -16, 13, 34, -171, 446, -972, 2989, 5969, -23, -217, 246, -183, 100, -40, 10 }, { 7, -16, 13, 34, -171, 447, -971, 2982, 5970, -18, -219, 247, -183, 100, -40, 10 }, { 7, -16, 13, 35, -172, 447, -971, 2975, 5974, -14, -221, 248, -183, 100, -40, 10 }, { 7, -16, 13, 35, -172, 447, -970, 2968, 5978, -10, -223, 249, -184, 100, -40, 10 }, { 7, -16, 12, 35, -173, 448, -970, 2961, 5981, -5, -224, 250, -184, 100, -40, 10 }, { 7, -16, 12, 36, -173, 448, -969, 2954, 5983, -1, -226, 251, -184, 100, -40, 10 }, { 6, -16, 12, 36, -173, 448, -969, 2947, 5989, 3, -228, 252, -185, 100, -40, 10 }, { 6, -16, 12, 36, -174, 449, -969, 2940, 5992, 8, -230, 253, -185, 100, -40, 10 }, { 6, -16, 12, 37, -174, 449, -968, 2934, 5993, 12, -232, 254, -185, 100, -40, 10 }, { 6, -16, 11, 37, -175, 449, -968, 2927, 6001, 16, -234, 254, -186, 100, -40, 10 }, { 6, -15, 11, 37, -175, 450, -967, 2920, 6001, 21, -236, 255, -186, 100, -40, 10 }, { 6, -15, 11, 38, -176, 450, -967, 2913, 6005, 25, -238, 256, -186, 100, -40, 10 }, { 6, -15, 11, 38, -176, 450, -966, 2906, 6009, 29, -240, 257, -187, 100, -40, 10 }, { 6, -15, 11, 38, -177, 451, -966, 2899, 6012, 34, -242, 258, -187, 100, -40, 10 }, { 6, -15, 11, 39, -177, 451, -965, 2892, 6013, 38, -244, 259, -187, 101, -40, 10 }, { 6, -15, 10, 39, -178, 451, -965, 2885, 6019, 43, -246, 260, -188, 101, -40, 10 }, { 6, -15, 10, 39, -178, 452, -964, 2878, 6021, 47, -248, 261, -188, 101, -40, 10 }, { 6, -15, 10, 40, -178, 452, -963, 2871, 6023, 51, -250, 262, -188, 101, -40, 10 }, { 6, -15, 10, 40, -179, 452, -963, 2864, 6028, 56, -251, 262, -189, 101, -40, 10 }, { 6, -15, 10, 40, -179, 453, -962, 2857, 6030, 60, -253, 263, -189, 101, -40, 10 }, { 6, -15, 9, 41, -180, 453, -962, 2850, 6034, 65, -255, 264, -189, 101, -40, 10 }, { 6, -15, 9, 41, -180, 453, -961, 2843, 6038, 69, -257, 265, -190, 101, -40, 10 }, { 6, -14, 9, 41, -181, 453, -961, 2837, 6040, 74, -259, 266, -190, 101, -40, 10 }, { 6, -14, 9, 42, -181, 454, -960, 2830, 6041, 78, -261, 267, -190, 101, -40, 10 }, { 6, -14, 9, 42, -181, 454, -960, 2823, 6044, 83, -263, 268, -190, 101, -40, 10 }, { 6, -14, 9, 42, -182, 454, -959, 2816, 6049, 87, -265, 269, -191, 101, -40, 10 }, { 6, -14, 8, 43, -182, 455, -958, 2809, 6051, 92, -267, 269, -191, 101, -40, 10 }, { 6, -14, 8, 43, -183, 455, -958, 2802, 6056, 96, -269, 270, -191, 101, -40, 10 }, { 6, -14, 8, 43, -183, 455, -957, 2795, 6059, 101, -271, 271, -192, 101, -40, 10 }, { 6, -14, 8, 43, -183, 455, -956, 2788, 6062, 105, -273, 272, -192, 101, -40, 10 }, { 6, -14, 8, 44, -184, 456, -956, 2781, 6064, 110, -275, 273, -192, 101, -40, 10 }, { 6, -14, 7, 44, -184, 456, -955, 2774, 6068, 114, -277, 274, -193, 102, -40, 10 }, { 6, -14, 7, 44, -185, 456, -955, 2767, 6072, 119, -279, 275, -193, 102, -40, 10 }, { 6, -14, 7, 45, -185, 456, -954, 2760, 6073, 123, -280, 276, -193, 102, -40, 10 }, { 6, -14, 7, 45, -186, 457, -953, 2753, 6077, 128, -282, 276, -194, 102, -40, 10 }, { 6, -13, 7, 45, -186, 457, -953, 2747, 6078, 133, -284, 277, -194, 102, -40, 10 }, { 6, -13, 7, 46, -186, 457, -952, 2740, 6080, 137, -286, 278, -194, 102, -40, 10 }, { 6, -13, 6, 46, -187, 457, -951, 2733, 6084, 142, -288, 279, -194, 102, -40, 10 }, { 6, -13, 6, 46, -187, 457, -951, 2726, 6088, 146, -290, 280, -195, 102, -39, 10 }, { 6, -13, 6, 47, -188, 458, -950, 2719, 6089, 151, -292, 281, -195, 102, -39, 10 }, { 6, -13, 6, 47, -188, 458, -949, 2712, 6091, 156, -294, 282, -195, 102, -39, 10 }, { 6, -13, 6, 47, -188, 458, -949, 2705, 6096, 160, -296, 283, -196, 102, -39, 10 }, { 6, -13, 6, 48, -189, 458, -948, 2698, 6099, 165, -298, 283, -196, 102, -39, 10 }, { 6, -13, 5, 48, -189, 459, -947, 2691, 6101, 170, -300, 284, -196, 102, -39, 10 }, { 6, -13, 5, 48, -189, 459, -946, 2684, 6105, 174, -302, 285, -197, 102, -39, 10 }, { 6, -13, 5, 48, -190, 459, -946, 2677, 6109, 179, -304, 286, -197, 102, -39, 10 }, { 5, -13, 5, 49, -190, 459, -945, 2671, 6110, 184, -306, 287, -197, 102, -39, 10 }, { 5, -12, 5, 49, -191, 459, -944, 2664, 6113, 188, -308, 288, -197, 102, -39, 10 }, { 5, -12, 5, 49, -191, 459, -944, 2657, 6118, 193, -310, 289, -198, 102, -39, 9 }, { 5, -12, 4, 50, -191, 460, -943, 2650, 6120, 198, -312, 289, -198, 102, -39, 9 }, { 5, -12, 4, 50, -192, 460, -942, 2643, 6123, 202, -313, 290, -198, 102, -39, 9 }, { 5, -12, 4, 50, -192, 460, -941, 2636, 6126, 207, -315, 291, -199, 102, -39, 9 }, { 5, -12, 4, 51, -192, 460, -941, 2629, 6128, 212, -317, 292, -199, 102, -39, 9 }, { 5, -12, 4, 51, -193, 460, -940, 2622, 6131, 217, -319, 293, -199, 102, -39, 9 }, { 5, -12, 3, 51, -193, 461, -939, 2615, 6133, 221, -321, 294, -199, 103, -39, 9 }, { 5, -12, 3, 52, -194, 461, -938, 2608, 6137, 226, -323, 294, -200, 103, -39, 9 }, { 5, -12, 3, 52, -194, 461, -937, 2601, 6139, 231, -325, 295, -200, 103, -39, 9 }, { 5, -12, 3, 52, -194, 461, -937, 2595, 6141, 236, -327, 296, -200, 103, -39, 9 }, { 5, -12, 3, 52, -195, 461, -936, 2588, 6146, 240, -329, 297, -201, 103, -39, 9 }, { 5, -12, 3, 53, -195, 461, -935, 2581, 6147, 245, -331, 298, -201, 103, -39, 9 }, { 5, -11, 2, 53, -195, 461, -934, 2574, 6149, 250, -333, 299, -201, 103, -39, 9 }, { 5, -11, 2, 53, -196, 462, -933, 2567, 6152, 255, -335, 299, -201, 103, -39, 9 }, { 5, -11, 2, 54, -196, 462, -933, 2560, 6155, 260, -337, 300, -202, 103, -39, 9 }, { 5, -11, 2, 54, -196, 462, -932, 2553, 6158, 264, -339, 301, -202, 103, -39, 9 }, { 5, -11, 2, 54, -197, 462, -931, 2546, 6161, 269, -341, 302, -202, 103, -39, 9 }, { 5, -11, 2, 54, -197, 462, -930, 2539, 6163, 274, -343, 303, -202, 103, -39, 9 }, { 5, -11, 1, 55, -197, 462, -929, 2533, 6165, 279, -345, 304, -203, 103, -39, 9 }, { 5, -11, 1, 55, -198, 462, -928, 2526, 6168, 284, -347, 304, -203, 103, -38, 9 }, { 5, -11, 1, 55, -198, 462, -928, 2519, 6171, 289, -349, 305, -203, 103, -38, 9 }, { 5, -11, 1, 56, -199, 463, -927, 2512, 6172, 294, -350, 306, -204, 103, -38, 9 }, { 5, -11, 1, 56, -199, 463, -926, 2505, 6175, 298, -352, 307, -204, 103, -38, 9 }, { 5, -11, 1, 56, -199, 463, -925, 2498, 6177, 303, -354, 308, -204, 103, -38, 9 }, { 5, -10, 0, 57, -200, 463, -924, 2491, 6179, 308, -356, 309, -204, 103, -38, 9 }, { 5, -10, 0, 57, -200, 463, -923, 2484, 6183, 313, -358, 309, -205, 103, -38, 9 }, { 5, -10, 0, 57, -200, 463, -922, 2478, 6184, 318, -360, 310, -205, 103, -38, 9 }, { 5, -10, 0, 57, -201, 463, -921, 2471, 6187, 323, -362, 311, -205, 103, -38, 9 }, { 5, -10, 0, 58, -201, 463, -920, 2464, 6188, 328, -364, 312, -205, 103, -38, 9 }, { 5, -10, 0, 58, -201, 463, -920, 2457, 6192, 333, -366, 313, -206, 103, -38, 9 }, { 5, -10, -1, 58, -201, 463, -919, 2450, 6195, 338, -368, 314, -206, 103, -38, 9 }, { 5, -10, -1, 58, -202, 464, -918, 2443, 6198, 343, -370, 314, -206, 103, -38, 9 }, { 5, -10, -1, 59, -202, 464, -917, 2436, 6199, 348, -372, 315, -206, 103, -38, 9 }, { 5, -10, -1, 59, -202, 464, -916, 2429, 6202, 353, -374, 316, -207, 103, -38, 9 }, { 5, -10, -1, 59, -203, 464, -915, 2423, 6204, 358, -376, 317, -207, 103, -38, 9 }, { 5, -10, -1, 60, -203, 464, -914, 2416, 6205, 363, -378, 318, -207, 103, -38, 9 }, { 5, -10, -2, 60, -203, 464, -913, 2409, 6209, 368, -380, 318, -207, 103, -38, 9 }, { 4, -9, -2, 60, -204, 464, -912, 2402, 6213, 373, -382, 319, -208, 103, -38, 9 }, { 4, -9, -2, 60, -204, 464, -911, 2395, 6214, 378, -383, 320, -208, 103, -38, 9 }, { 4, -9, -2, 61, -204, 464, -910, 2388, 6215, 383, -385, 321, -208, 103, -38, 9 }, { 4, -9, -2, 61, -205, 464, -909, 2382, 6217, 388, -387, 322, -208, 103, -38, 9 }, { 4, -9, -2, 61, -205, 464, -908, 2375, 6220, 393, -389, 322, -209, 103, -37, 9 }, { 4, -9, -3, 62, -205, 464, -907, 2368, 6222, 398, -391, 323, -209, 103, -37, 9 }, { 4, -9, -3, 62, -205, 464, -906, 2361, 6225, 403, -393, 324, -209, 103, -37, 8 }, { 4, -9, -3, 62, -206, 464, -905, 2354, 6228, 408, -395, 325, -209, 103, -37, 8 }, { 4, -9, -3, 62, -206, 464, -904, 2347, 6231, 413, -397, 326, -210, 103, -37, 8 }, { 4, -9, -3, 63, -206, 464, -903, 2340, 6233, 418, -399, 326, -210, 103, -37, 8 }, { 4, -9, -3, 63, -207, 464, -902, 2334, 6235, 423, -401, 327, -210, 103, -37, 8 }, { 4, -9, -4, 63, -207, 464, -901, 2327, 6237, 429, -403, 328, -210, 103, -37, 8 }, { 4, -8, -4, 63, -207, 464, -900, 2320, 6239, 434, -405, 329, -211, 103, -37, 8 }, { 4, -8, -4, 64, -208, 464, -899, 2313, 6241, 439, -407, 330, -211, 103, -37, 8 }, { 4, -8, -4, 64, -208, 464, -898, 2306, 6244, 444, -409, 330, -211, 103, -37, 8 }, { 4, -8, -4, 64, -208, 464, -897, 2300, 6245, 449, -411, 331, -211, 103, -37, 8 }, { 4, -8, -4, 64, -208, 464, -896, 2293, 6247, 454, -413, 332, -211, 103, -37, 8 }, { 4, -8, -4, 65, -209, 464, -895, 2286, 6249, 459, -414, 333, -212, 103, -37, 8 }, { 4, -8, -5, 65, -209, 464, -894, 2279, 6252, 465, -416, 333, -212, 103, -37, 8 }, { 4, -8, -5, 65, -209, 464, -893, 2272, 6254, 470, -418, 334, -212, 103, -37, 8 }, { 4, -8, -5, 65, -209, 464, -892, 2265, 6256, 475, -420, 335, -212, 103, -37, 8 }, { 4, -8, -5, 66, -210, 464, -891, 2259, 6258, 480, -422, 336, -213, 103, -37, 8 }, { 4, -8, -5, 66, -210, 464, -890, 2252, 6260, 485, -424, 337, -213, 103, -37, 8 }, { 4, -8, -5, 66, -210, 464, -889, 2245, 6262, 490, -426, 337, -213, 103, -36, 8 }, { 4, -8, -6, 67, -210, 464, -887, 2238, 6262, 496, -428, 338, -213, 103, -36, 8 }, { 4, -7, -6, 67, -211, 464, -886, 2231, 6264, 501, -430, 339, -213, 103, -36, 8 }, { 4, -7, -6, 67, -211, 464, -885, 2225, 6266, 506, -432, 340, -214, 103, -36, 8 }, { 4, -7, -6, 67, -211, 464, -884, 2218, 6269, 511, -434, 340, -214, 103, -36, 8 }, { 4, -7, -6, 68, -212, 464, -883, 2211, 6270, 517, -436, 341, -214, 103, -36, 8 }, { 4, -7, -6, 68, -212, 464, -882, 2204, 6272, 522, -438, 342, -214, 103, -36, 8 }, { 4, -7, -7, 68, -212, 464, -881, 2198, 6274, 527, -440, 343, -214, 103, -36, 8 }, { 4, -7, -7, 68, -212, 464, -880, 2191, 6277, 532, -441, 343, -215, 103, -36, 8 }, { 4, -7, -7, 69, -213, 464, -879, 2184, 6278, 538, -443, 344, -215, 103, -36, 8 }, { 4, -7, -7, 69, -213, 464, -877, 2177, 6279, 543, -445, 345, -215, 103, -36, 8 }, { 4, -7, -7, 69, -213, 464, -876, 2170, 6281, 548, -447, 346, -215, 103, -36, 8 }, { 4, -7, -7, 69, -213, 464, -875, 2164, 6282, 554, -449, 346, -215, 103, -36, 8 }, { 4, -7, -7, 70, -214, 464, -874, 2157, 6285, 559, -451, 347, -216, 103, -36, 8 }, { 4, -6, -8, 70, -214, 463, -873, 2150, 6288, 564, -453, 348, -216, 103, -36, 8 }, { 3, -6, -8, 70, -214, 463, -872, 2143, 6291, 569, -455, 349, -216, 103, -36, 8 }, { 3, -6, -8, 70, -214, 463, -871, 2137, 6292, 575, -457, 349, -216, 103, -36, 8 }, { 3, -6, -8, 70, -214, 463, -869, 2130, 6292, 580, -459, 350, -216, 103, -35, 8 }, { 3, -6, -8, 71, -215, 463, -868, 2123, 6294, 586, -461, 351, -217, 103, -35, 8 }, { 3, -6, -8, 71, -215, 463, -867, 2116, 6297, 591, -463, 352, -217, 103, -35, 7 }, { 3, -6, -9, 71, -215, 463, -866, 2110, 6300, 596, -465, 352, -217, 103, -35, 7 }, { 3, -6, -9, 71, -215, 463, -865, 2103, 6300, 602, -466, 353, -217, 103, -35, 7 }, { 3, -6, -9, 72, -216, 463, -863, 2096, 6301, 607, -468, 354, -217, 103, -35, 7 }, { 3, -6, -9, 72, -216, 463, -862, 2089, 6304, 612, -470, 355, -218, 103, -35, 7 }, { 3, -6, -9, 72, -216, 463, -861, 2083, 6305, 618, -472, 355, -218, 103, -35, 7 }, { 3, -6, -9, 72, -216, 462, -860, 2076, 6308, 623, -474, 356, -218, 103, -35, 7 }, { 3, -6, -9, 73, -216, 462, -859, 2069, 6308, 629, -476, 357, -218, 103, -35, 7 }, { 3, -5, -10, 73, -217, 462, -857, 2063, 6309, 634, -478, 358, -218, 103, -35, 7 }, { 3, -5, -10, 73, -217, 462, -856, 2056, 6313, 639, -480, 358, -219, 103, -35, 7 }, { 3, -5, -10, 73, -217, 462, -855, 2049, 6314, 645, -482, 359, -219, 103, -35, 7 }, { 3, -5, -10, 74, -217, 462, -854, 2042, 6315, 650, -484, 360, -219, 103, -35, 7 }, { 3, -5, -10, 74, -217, 462, -853, 2036, 6316, 656, -486, 360, -219, 103, -35, 7 }, { 3, -5, -10, 74, -218, 462, -851, 2029, 6316, 661, -487, 361, -219, 103, -34, 7 }, { 3, -5, -10, 74, -218, 461, -850, 2022, 6318, 667, -489, 362, -219, 103, -34, 7 }, { 3, -5, -11, 74, -218, 461, -849, 2016, 6321, 672, -491, 363, -220, 103, -34, 7 }, { 3, -5, -11, 75, -218, 461, -848, 2009, 6322, 678, -493, 363, -220, 103, -34, 7 }, { 3, -5, -11, 75, -219, 461, -846, 2002, 6324, 683, -495, 364, -220, 103, -34, 7 }, { 3, -5, -11, 75, -219, 461, -845, 1995, 6326, 689, -497, 365, -220, 102, -34, 7 }, { 3, -5, -11, 75, -219, 461, -844, 1989, 6328, 694, -499, 365, -220, 102, -34, 7 }, { 3, -5, -11, 76, -219, 461, -842, 1982, 6327, 700, -501, 366, -220, 102, -34, 7 }, { 3, -4, -11, 76, -219, 460, -841, 1975, 6330, 705, -503, 367, -221, 102, -34, 7 }, { 3, -4, -12, 76, -219, 460, -840, 1969, 6330, 711, -504, 368, -221, 102, -34, 7 }, { 3, -4, -12, 76, -220, 460, -839, 1962, 6334, 716, -506, 368, -221, 102, -34, 7 }, { 3, -4, -12, 76, -220, 460, -837, 1955, 6334, 722, -508, 369, -221, 102, -34, 7 }, { 3, -4, -12, 77, -220, 460, -836, 1949, 6334, 727, -510, 370, -221, 102, -34, 7 }, { 3, -4, -12, 77, -220, 460, -835, 1942, 6336, 733, -512, 370, -221, 102, -34, 7 }, { 3, -4, -12, 77, -220, 459, -833, 1935, 6338, 738, -514, 371, -221, 102, -34, 7 }, { 3, -4, -12, 77, -221, 459, -832, 1929, 6339, 744, -516, 372, -222, 102, -33, 7 }, { 3, -4, -13, 78, -221, 459, -831, 1922, 6342, 749, -518, 372, -222, 102, -33, 7 }, { 3, -4, -13, 78, -221, 459, -829, 1915, 6342, 755, -520, 373, -222, 102, -33, 7 }, { 3, -4, -13, 78, -221, 459, -828, 1909, 6341, 761, -521, 374, -222, 102, -33, 7 }, { 3, -4, -13, 78, -221, 458, -827, 1902, 6346, 766, -523, 374, -222, 102, -33, 6 }, { 2, -4, -13, 78, -221, 458, -825, 1896, 6346, 772, -525, 375, -222, 102, -33, 6 }, { 2, -3, -13, 79, -222, 458, -824, 1889, 6348, 777, -527, 376, -223, 102, -33, 6 }, { 2, -3, -13, 79, -222, 458, -823, 1882, 6350, 783, -529, 376, -223, 102, -33, 6 }, { 2, -3, -14, 79, -222, 458, -821, 1876, 6350, 789, -531, 377, -223, 102, -33, 6 }, { 2, -3, -14, 79, -222, 457, -820, 1869, 6353, 794, -533, 378, -223, 102, -33, 6 }, { 2, -3, -14, 79, -222, 457, -819, 1862, 6355, 800, -535, 378, -223, 102, -33, 6 }, { 2, -3, -14, 80, -222, 457, -817, 1856, 6352, 806, -536, 379, -223, 102, -33, 6 }, { 2, -3, -14, 80, -223, 457, -816, 1849, 6356, 811, -538, 380, -223, 101, -33, 6 }, { 2, -3, -14, 80, -223, 457, -815, 1843, 6356, 817, -540, 380, -223, 101, -32, 6 }, { 2, -3, -14, 80, -223, 456, -813, 1836, 6358, 823, -542, 381, -224, 101, -32, 6 }, { 2, -3, -15, 80, -223, 456, -812, 1829, 6361, 828, -544, 382, -224, 101, -32, 6 }, { 2, -3, -15, 81, -223, 456, -811, 1823, 6361, 834, -546, 382, -224, 101, -32, 6 }, { 2, -3, -15, 81, -223, 456, -809, 1816, 6361, 840, -548, 383, -224, 101, -32, 6 }, { 2, -3, -15, 81, -223, 455, -808, 1810, 6362, 845, -549, 384, -224, 101, -32, 6 }, { 2, -2, -15, 81, -224, 455, -806, 1803, 6363, 851, -551, 384, -224, 101, -32, 6 }, { 2, -2, -15, 81, -224, 455, -805, 1796, 6364, 857, -553, 385, -224, 101, -32, 6 }, { 2, -2, -15, 82, -224, 455, -804, 1790, 6364, 862, -555, 386, -224, 101, -32, 6 }, { 2, -2, -16, 82, -224, 454, -802, 1783, 6368, 868, -557, 386, -225, 101, -32, 6 }, { 2, -2, -16, 82, -224, 454, -801, 1777, 6368, 874, -559, 387, -225, 101, -32, 6 }, { 2, -2, -16, 82, -224, 454, -799, 1770, 6367, 880, -560, 388, -225, 101, -32, 6 }, { 2, -2, -16, 82, -224, 454, -798, 1764, 6368, 885, -562, 388, -225, 101, -31, 6 }, { 2, -2, -16, 83, -224, 453, -796, 1757, 6368, 891, -564, 389, -225, 101, -31, 6 }, { 2, -2, -16, 83, -225, 453, -795, 1751, 6370, 897, -566, 389, -225, 101, -31, 6 }, { 2, -2, -16, 83, -225, 453, -794, 1744, 6372, 903, -568, 390, -225, 100, -31, 6 }, { 2, -2, -16, 83, -225, 453, -792, 1737, 6373, 908, -570, 391, -225, 100, -31, 6 }, { 2, -2, -17, 83, -225, 452, -791, 1731, 6375, 914, -571, 391, -225, 100, -31, 6 }, { 2, -2, -17, 84, -225, 452, -789, 1724, 6375, 920, -573, 392, -226, 100, -31, 6 }, { 2, -2, -17, 84, -225, 452, -788, 1718, 6375, 926, -575, 393, -226, 100, -31, 6 }, { 2, -1, -17, 84, -225, 451, -786, 1711, 6376, 932, -577, 393, -226, 100, -31, 6 }, { 2, -1, -17, 84, -225, 451, -785, 1705, 6378, 937, -579, 394, -226, 100, -31, 5 }, { 2, -1, -17, 84, -226, 451, -783, 1698, 6380, 943, -581, 394, -226, 100, -31, 5 }, { 2, -1, -17, 84, -226, 451, -782, 1692, 6379, 949, -582, 395, -226, 100, -31, 5 }, { 2, -1, -18, 85, -226, 450, -781, 1685, 6380, 955, -584, 396, -226, 100, -30, 5 }, { 2, -1, -18, 85, -226, 450, -779, 1679, 6380, 961, -586, 396, -226, 100, -30, 5 }, { 2, -1, -18, 85, -226, 450, -778, 1672, 6381, 967, -588, 397, -226, 100, -30, 5 }, { 2, -1, -18, 85, -226, 449, -776, 1666, 6382, 972, -590, 398, -226, 100, -30, 5 }, { 1, -1, -18, 85, -226, 449, -775, 1659, 6386, 978, -592, 398, -227, 100, -30, 5 }, { 1, -1, -18, 86, -226, 449, -773, 1653, 6384, 984, -593, 399, -227, 99, -30, 5 }, { 1, -1, -18, 86, -226, 448, -772, 1646, 6387, 990, -595, 399, -227, 99, -30, 5 }, { 1, -1, -18, 86, -226, 448, -770, 1640, 6386, 996, -597, 400, -227, 99, -30, 5 }, { 1, -1, -19, 86, -227, 448, -769, 1633, 6390, 1002, -599, 400, -227, 99, -30, 5 }, { 1, 0, -19, 86, -227, 447, -767, 1627, 6389, 1008, -601, 401, -227, 99, -30, 5 }, { 1, 0, -19, 86, -227, 447, -766, 1621, 6388, 1014, -602, 402, -227, 99, -30, 5 }, { 1, 0, -19, 87, -227, 447, -764, 1614, 6388, 1019, -604, 402, -227, 99, -29, 5 }, { 1, 0, -19, 87, -227, 446, -763, 1608, 6389, 1025, -606, 403, -227, 99, -29, 5 }, { 1, 0, -19, 87, -227, 446, -761, 1601, 6390, 1031, -608, 403, -227, 99, -29, 5 }, { 1, 0, -19, 87, -227, 446, -760, 1595, 6390, 1037, -610, 404, -227, 99, -29, 5 }, { 1, 0, -19, 87, -227, 445, -758, 1588, 6390, 1043, -611, 405, -227, 99, -29, 5 }, { 1, 0, -20, 87, -227, 445, -757, 1582, 6393, 1049, -613, 405, -228, 99, -29, 5 }, { 1, 0, -20, 88, -227, 445, -755, 1575, 6393, 1055, -615, 406, -228, 98, -29, 5 }, { 1, 0, -20, 88, -227, 444, -753, 1569, 6394, 1061, -617, 406, -228, 98, -29, 5 }, { 1, 0, -20, 88, -227, 444, -752, 1563, 6393, 1067, -618, 407, -228, 98, -29, 5 }, { 1, 0, -20, 88, -228, 444, -750, 1556, 6395, 1073, -620, 407, -228, 98, -29, 5 }, { 1, 0, -20, 88, -228, 443, -749, 1550, 6396, 1079, -622, 408, -228, 98, -29, 5 }, { 1, 0, -20, 88, -228, 443, -747, 1543, 6395, 1085, -624, 409, -228, 98, -28, 5 }, { 1, 1, -20, 89, -228, 443, -746, 1537, 6394, 1091, -626, 409, -228, 98, -28, 5 }, { 1, 1, -21, 89, -228, 442, -744, 1531, 6395, 1097, -627, 410, -228, 98, -28, 4 }, { 1, 1, -21, 89, -228, 442, -743, 1524, 6397, 1103, -629, 410, -228, 98, -28, 4 }, { 1, 1, -21, 89, -228, 441, -741, 1518, 6397, 1109, -631, 411, -228, 98, -28, 4 }, { 1, 1, -21, 89, -228, 441, -739, 1511, 6399, 1115, -633, 411, -228, 97, -28, 4 }, { 1, 1, -21, 89, -228, 441, -738, 1505, 6398, 1121, -634, 412, -228, 97, -28, 4 }, { 1, 1, -21, 90, -228, 440, -736, 1499, 6398, 1127, -636, 412, -228, 97, -28, 4 }, { 1, 1, -21, 90, -228, 440, -735, 1492, 6399, 1133, -638, 413, -228, 97, -28, 4 }, { 1, 1, -21, 90, -228, 440, -733, 1486, 6399, 1139, -640, 413, -228, 97, -28, 4 }, { 1, 1, -22, 90, -228, 439, -732, 1480, 6399, 1145, -641, 414, -228, 97, -27, 4 }, { 1, 1, -22, 90, -228, 439, -730, 1473, 6399, 1151, -643, 415, -228, 97, -27, 4 }, { 1, 1, -22, 90, -228, 438, -728, 1467, 6400, 1157, -645, 415, -228, 97, -27, 4 }, { 1, 1, -22, 90, -228, 438, -727, 1461, 6401, 1163, -647, 416, -229, 97, -27, 4 }, { 1, 1, -22, 91, -228, 438, -725, 1454, 6400, 1169, -648, 416, -229, 97, -27, 4 }, { 1, 2, -22, 91, -228, 437, -724, 1448, 6401, 1175, -650, 417, -229, 96, -27, 4 }, { 1, 2, -22, 91, -229, 437, -722, 1442, 6402, 1181, -652, 417, -229, 96, -27, 4 }, { 1, 2, -22, 91, -229, 436, -720, 1435, 6403, 1187, -654, 418, -229, 96, -27, 4 }, { 1, 2, -22, 91, -229, 436, -719, 1429, 6403, 1193, -655, 418, -229, 96, -27, 4 }, { 0, 2, -23, 91, -229, 435, -717, 1423, 6404, 1200, -657, 419, -229, 96, -27, 4 }, { 0, 2, -23, 91, -229, 435, -716, 1416, 6405, 1206, -659, 419, -229, 96, -26, 4 }, { 0, 2, -23, 92, -229, 435, -714, 1410, 6402, 1212, -660, 420, -229, 96, -26, 4 }, { 0, 2, -23, 92, -229, 434, -712, 1404, 6403, 1218, -662, 420, -229, 96, -26, 4 }, { 0, 2, -23, 92, -229, 434, -711, 1398, 6403, 1224, -664, 421, -229, 96, -26, 4 }, { 0, 2, -23, 92, -229, 433, -709, 1391, 6406, 1230, -666, 421, -229, 95, -26, 4 }, { 0, 2, -23, 92, -229, 433, -707, 1385, 6404, 1236, -667, 422, -229, 95, -26, 4 }, { 0, 2, -23, 92, -229, 432, -706, 1379, 6406, 1242, -669, 422, -229, 95, -26, 4 }, { 0, 2, -24, 92, -229, 432, -704, 1373, 6406, 1249, -671, 423, -229, 95, -26, 3 }, { 0, 2, -24, 93, -229, 432, -702, 1366, 6405, 1255, -672, 423, -229, 95, -26, 3 }, { 0, 3, -24, 93, -229, 431, -701, 1360, 6405, 1261, -674, 424, -229, 95, -26, 3 }, { 0, 3, -24, 93, -229, 431, -699, 1354, 6404, 1267, -676, 424, -229, 95, -25, 3 }, { 0, 3, -24, 93, -229, 430, -698, 1348, 6404, 1273, -677, 425, -229, 95, -25, 3 }, { 0, 3, -24, 93, -229, 430, -696, 1341, 6406, 1279, -679, 425, -229, 94, -25, 3 }, { 0, 3, -24, 93, -229, 429, -694, 1335, 6406, 1285, -681, 426, -229, 94, -25, 3 }, { 0, 3, -24, 93, -229, 429, -693, 1329, 6405, 1292, -682, 426, -229, 94, -25, 3 }, { 0, 3, -24, 94, -229, 428, -691, 1323, 6404, 1298, -684, 427, -229, 94, -25, 3 }, { 0, 3, -25, 94, -229, 428, -689, 1316, 6406, 1304, -686, 427, -229, 94, -25, 3 }, }; openmsx-0.10.0/src/sound/BlipConfig.hh0000644000175000017500000000117212262345041020321 0ustar manuelmanuel00000000000000#ifndef BLIPCONFIG_HH #define BLIPCONFIG_HH // Number of bits in phase offset. Fewer than 6 bits (64 phase offsets) results // in noticeable broadband noise when synthesizing high frequency square waves. static const int BLIP_PHASE_BITS = 10; // The input sample stream can only use this many bits out of the available 32 // bits. So 29 bits means the sample values must be in range [-256M, 256M]. static const int BLIP_SAMPLE_BITS = 29; // Number of samples in a (pre-calculated) impulse-response wave-form. static const int BLIP_IMPULSE_WIDTH = 16; // Derived constants static const int BLIP_RES = 1 << BLIP_PHASE_BITS; #endif openmsx-0.10.0/src/sound/LibAOSoundDriver.cc0000644000175000017500000000227012262345041021406 0ustar manuelmanuel00000000000000#include "LibAOSoundDriver.hh" #include "MSXException.hh" #include #include namespace openmsx { static const int BITS_PER_SAMPLE = 16; static const int CHANNELS = 2; LibAOSoundDriver::LibAOSoundDriver(unsigned sampleRate_, unsigned bufferSize_) : sampleRate(sampleRate_) , bufferSize(bufferSize_) { ao_initialize(); int driver = ao_default_driver_id(); ao_sample_format format; memset(&format, 0, sizeof(format)); format.bits = BITS_PER_SAMPLE; format.channels = CHANNELS; format.rate = sampleRate; format.byte_format = AO_FMT_NATIVE; device = ao_open_live(driver, &format, nullptr /* no options */); if (!device) { throw MSXException("Couldn't open audio device"); } } LibAOSoundDriver::~LibAOSoundDriver() { ao_close(device); ao_shutdown(); } void LibAOSoundDriver::mute() { } void LibAOSoundDriver::unmute() { } unsigned LibAOSoundDriver::getFrequency() const { return sampleRate; } unsigned LibAOSoundDriver::getSamples() const { return bufferSize; } void LibAOSoundDriver::uploadBuffer(short* buffer, unsigned len) { ao_play(device, reinterpret_cast(buffer), len * CHANNELS * (BITS_PER_SAMPLE / 8)); } } // namespace openmsx openmsx-0.10.0/src/sound/MSXAudio.cc0000644000175000017500000000663312262345041017733 0ustar manuelmanuel00000000000000#include "MSXAudio.hh" #include "Y8950.hh" #include "Y8950Periphery.hh" #include "DACSound8U.hh" #include "MSXMotherBoard.hh" #include "StringOp.hh" #include "serialize.hh" #include "memory.hh" using std::string; namespace openmsx { // MSXAudio MSXAudio::MSXAudio(const DeviceConfig& config) : MSXDevice(config) , dacValue(0x80), dacEnabled(false) { string type(StringOp::toLower(config.getChildData("type", "philips"))); if (type == "philips") { dac = make_unique( getName() + " 8-bit DAC", "MSX-AUDIO 8-bit DAC", config); } int ramSize = config.getChildDataAsInt("sampleram", 256); // size in kb EmuTime::param time = getCurrentTime(); y8950 = make_unique( getName(), config, ramSize * 1024, time, *this); powerUp(time); } MSXAudio::~MSXAudio() { // delete soon, because PanasonicAudioPeriphery still uses // this object in its destructor periphery.reset(); } Y8950Periphery& MSXAudio::createPeriphery(const string& soundDeviceName) { periphery = Y8950PeripheryFactory::create( *this, getDeviceConfig2(), soundDeviceName); return *periphery; } void MSXAudio::powerUp(EmuTime::param time) { y8950->clearRam(); reset(time); } void MSXAudio::reset(EmuTime::param time) { y8950->reset(time); periphery->reset(); registerLatch = 0; // TODO check } byte MSXAudio::readIO(word port, EmuTime::param time) { byte result; if ((port & 0xFF) == 0x0A) { // read DAC result = 255; } else { result = (port & 1) ? y8950->readReg(registerLatch, time) : y8950->readStatus(time); } return result; } byte MSXAudio::peekIO(word port, EmuTime::param time) const { if ((port & 0xFF) == 0x0A) { // read DAC return 255; // read always returns 255 } else { return (port & 1) ? y8950->peekReg(registerLatch, time) : y8950->peekStatus(time); } } void MSXAudio::writeIO(word port, byte value, EmuTime::param time) { if ((port & 0xFF) == 0x0A) { dacValue = value; if (dacEnabled) { assert(dac); dac->writeDAC(dacValue, time); } } else if ((port & 0x01) == 0) { // 0xC0 or 0xC2 registerLatch = value; } else { // 0xC1 or 0xC3 y8950->writeReg(registerLatch, value, time); } } byte MSXAudio::readMem(word address, EmuTime::param time) { return periphery->readMem(address, time); } byte MSXAudio::peekMem(word address, EmuTime::param time) const { return periphery->peekMem(address, time); } void MSXAudio::writeMem(word address, byte value, EmuTime::param time) { periphery->writeMem(address, value, time); } const byte* MSXAudio::getReadCacheLine(word start) const { return periphery->getReadCacheLine(start); } byte* MSXAudio::getWriteCacheLine(word start) const { return periphery->getWriteCacheLine(start); } void MSXAudio::enableDAC(bool enable, EmuTime::param time) { if ((dacEnabled != enable) && dac) { dacEnabled = enable; byte value = dacEnabled ? dacValue : 0x80; dac->writeDAC(value, time); } } template void MSXAudio::serialize(Archive& ar, unsigned /*version*/) { ar.serializePolymorphic("periphery", *periphery); ar.serialize("Y8950", *y8950); ar.serialize("registerLatch", registerLatch); ar.serialize("dacValue", dacValue); ar.serialize("dacEnabled", dacEnabled); if (ar.isLoader()) { // restore dac status if (dacEnabled) { assert(dac); dac->writeDAC(dacValue, getCurrentTime()); } } } INSTANTIATE_SERIALIZE_METHODS(MSXAudio); REGISTER_MSXDEVICE(MSXAudio, "MSX-Audio"); } // namespace openmsx openmsx-0.10.0/src/sound/SamplePlayer.hh0000644000175000017500000000303212262345041020700 0ustar manuelmanuel00000000000000#ifndef SAMPLEPLAYER_HH #define SAMPLEPLAYER_HH #include "ResampledSoundDevice.hh" #include "WavData.hh" #include #include namespace openmsx { class MSXMotherBoard; class SamplePlayer : public ResampledSoundDevice { public: SamplePlayer(const std::string& name, const std::string& desc, const DeviceConfig& config, const std::string& samplesBaseName, unsigned numSamples, const std::string& alternativeName = ""); ~SamplePlayer(); void reset(); /** Start playing a (new) sample. */ void play(unsigned sampleNum); /** Keep on repeating the given sample data. * If there is already a sample playing, that sample is still * finished. If there was no sample playing, the given sample * immediatly starts playing. * Parameters are the same as for the play() method. * @see stopRepeat() */ void repeat(unsigned sampleNum); /** Stop repeat mode. * The currently playing sample will still be finished, but won't * be started. * @see repeat() */ void stopRepeat(); /** Is there currently playing a sample. */ bool isPlaying() const; template void serialize(Archive& ar, unsigned version); private: inline int getSample(unsigned index); void setWavParams(); void doRepeat(); // SoundDevice virtual void generateChannels(int** bufs, unsigned num); std::vector samples; const void* sampBuf; unsigned index; unsigned bufferSize; unsigned currentSampleNum; unsigned nextSampleNum; bool bits8; }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/YMF262.hh0000644000175000017500000000144312262345041017173 0ustar manuelmanuel00000000000000#ifndef YMF262_HH #define YMF262_HH #include "EmuTime.hh" #include "openmsx.hh" #include "serialize_meta.hh" #include #include namespace openmsx { class MSXMotherBoard; class DeviceConfig; class YMF262 { public: YMF262(const std::string& name, const DeviceConfig& config, bool isYMF278); ~YMF262(); void reset(EmuTime::param time); void writeReg (unsigned r, byte v, EmuTime::param time); void writeReg512(unsigned r, byte v, EmuTime::param time); byte readReg(unsigned reg); byte peekReg(unsigned reg) const; byte readStatus(); byte peekStatus() const; template void serialize(Archive& ar, unsigned version); private: class Impl; const std::unique_ptr pimpl; }; SERIALIZE_CLASS_VERSION(YMF262, 2); } // namespace openmsx #endif openmsx-0.10.0/src/sound/YM2413Okazaki.hh0000644000175000017500000001245512262345041020464 0ustar manuelmanuel00000000000000#ifndef YM2413OKAZAKI_HH #define YM2413OKAZAKI_HH #include "YM2413Core.hh" #include "FixedPoint.hh" #include "serialize_meta.hh" namespace openmsx { namespace YM2413Okazaki { // Constants (shared between this class and table generator) #include "YM2413OkazakiConfig.hh" class YM2413; typedef FixedPoint EnvPhaseIndex; enum EnvelopeState { ATTACK, DECAY, SUSHOLD, SUSTAIN, RELEASE, SETTLE, FINISH }; class Patch { public: /** Creates an uninitialized Patch object; call initXXX() before use. * This approach makes it possible to create an array of Patches. */ Patch(); void initModulator(const byte* data); void initCarrier (const byte* data); /** Sets the Key Scale of Rate (0 or 1). */ inline void setKR(byte value); /** Sets the frequency multiplier factor [0..15]. */ inline void setML(byte value); /** Sets Key scale level [0..3]. */ inline void setKL(byte value); /** Set volume (total level) [0..63]. */ inline void setTL(byte value); /** Set waveform [0..1]. */ inline void setWF(byte value); /** Sets the amount of feedback [0..7]. */ inline void setFB(byte value); /** Sets sustain level [0..15]. */ inline void setSL(byte value); unsigned* WF; // 0-1 transformed to waveform[0-1] byte* KL; // 0-3 transformed to tllTable[0-3] unsigned SL; // 0-15 transformed to slTable[0-15] byte AMPM; // 0-3 2 packed booleans bool EG; // 0-1 byte KR; // 0-1 transformed to 10,8 byte ML; // 0-15 transformed to mlTable[0-15] byte TL; // 0-63 transformed to TL2EG(0..63) == [0..252] byte FB; // 0,1-7 transformed to 0,7-1 byte AR; // 0-15 byte DR; // 0-15 byte RR; // 0-15 }; class Slot { public: void reset(); inline void setEnvelopeState(EnvelopeState state); inline bool isActive() const; inline void slotOn(); inline void slotOn2(); inline void slotOff(); inline void setPatch(Patch& patch); inline void setVolume(unsigned volume); inline unsigned calc_phase(unsigned lfo_pm); template inline unsigned calc_envelope(int lfo_am, unsigned fixed_env); template unsigned calc_fixed_env() const; void calc_envelope_outline(unsigned& out); template inline int calc_slot_car(unsigned lfo_pm, int lfo_am, int fm, unsigned fixed_env); template inline int calc_slot_mod(unsigned lfo_pm, int lfo_am, unsigned fixed_env); inline int calc_slot_tom(); inline int calc_slot_snare(bool noise); inline int calc_slot_cym(unsigned phase7, unsigned phase8); inline int calc_slot_hat(unsigned phase7, unsigned phase8, bool noise); inline void updatePG(unsigned freq); inline void updateTLL(unsigned freq, bool actAsCarrier); inline void updateRKS(unsigned freq); inline void updateEG(); inline void updateAll(unsigned freq, bool actAsCarrier); template void serialize(Archive& ar, unsigned version); // OUTPUT int feedback; int output; // Output value of slot // for Phase Generator (PG) unsigned cphase; // Phase counter unsigned dphase[8]; // Phase increment // for Envelope Generator (EG) unsigned volume; // Current volume unsigned tll; // Total Level + Key scale level int* dphaseDRTableRks; // (converted to EnvPhaseIndex) EnvelopeState state; // Current state EnvPhaseIndex eg_phase; // Phase EnvPhaseIndex eg_dphase;// Phase increment amount EnvPhaseIndex eg_phase_max; byte slot_on_flag; bool sustain; // Sustain Patch patch; Slot* sibling; // pointer to sibling slot (only valid for car -> mod) }; class Channel { public: Channel(); void reset(YM2413& global); inline void setPatch(unsigned num, YM2413& global); inline void setSustain(bool sustain, bool modActAsCarrier); inline void keyOn(); inline void keyOff(); Slot mod, car; template void serialize(Archive& ar, unsigned version); }; class YM2413 : public YM2413Core { public: YM2413(); inline void keyOn_BD(); inline void keyOn_SD(); inline void keyOn_TOM(); inline void keyOn_HH(); inline void keyOn_CYM(); inline void keyOff_BD(); inline void keyOff_SD(); inline void keyOff_TOM(); inline void keyOff_HH(); inline void keyOff_CYM(); inline void setRhythmFlags(byte old); inline void update_key_status(); inline bool isRhythm() const; inline unsigned getFreq(unsigned channel) const; Patch& getPatch(unsigned instrument, bool carrier); template inline void calcChannel(Channel& ch, int* buf, unsigned num); template void serialize(Archive& ar, unsigned version); private: // YM2413Core virtual void reset(); virtual void writeReg(byte reg, byte value); virtual byte peekReg(byte reg) const; virtual void generateChannels(int* bufs[9 + 5], unsigned num); virtual int getAmplificationFactor() const; /** Channel & Slot */ Channel channels[9]; /** Pitch Modulator */ unsigned pm_phase; /** Amp Modulator */ unsigned am_phase; /** Noise Generator */ unsigned noise_seed; /** Number of samples the output was completely silent. */ unsigned idleSamples; /** Voice Data */ Patch patches[19][2]; /** Registers */ byte reg[0x40]; }; } // namespace YM2413Okazaki SERIALIZE_CLASS_VERSION(YM2413Okazaki::Slot, 4); SERIALIZE_CLASS_VERSION(YM2413Okazaki::Channel, 2); SERIALIZE_CLASS_VERSION(YM2413Okazaki::YM2413, 3); } // namespace openmsx #endif openmsx-0.10.0/src/sound/ResampleHQ.cc0000644000175000017500000003030712262345041020276 0ustar manuelmanuel00000000000000// Based on libsamplerate-0.1.2 (aka Secret Rabit Code) // // simplified code in several ways: // - resample algorithm is no longer switchable, we took this variant: // Band limited sinc interpolation, fastest, 97dB SNR, 80% BW // - don't allow to change sample rate on-the-fly // - assume input (and thus also output) signals have infinte length, so // there is no special code to handle the ending of the signal // - changed/simplified API to better match openmsx use model // (e.g. remove all error checking) #include "ResampleHQ.hh" #include "ResampledSoundDevice.hh" #include "FixedPoint.hh" #include "MemoryOps.hh" #include "noncopyable.hh" #include "vla.hh" #include "countof.hh" #include "likely.hh" #include "build-info.hh" #include #include #include #include #include #include #ifdef __SSE2__ #include #endif namespace openmsx { // Note: without appending 'f' to the values in ResampleCoeffs.ii, // this will generate thousands of C4305 warnings in VC++ // E.g. warning C4305: 'initializing' : truncation from 'double' to 'const float' static const float coeffs[] = { #include "ResampleCoeffs.ii" }; static const int INDEX_INC = 128; static const int COEFF_LEN = countof(coeffs); static const int COEFF_HALF_LEN = COEFF_LEN - 1; static const unsigned TAB_LEN = 4096; class ResampleCoeffs : private noncopyable { public: static ResampleCoeffs& instance(); void getCoeffs(double ratio, float*& table, unsigned& filterLen); void releaseCoeffs(double ratio); private: typedef FixedPoint<16> FilterIndex; ResampleCoeffs(); ~ResampleCoeffs(); double getCoeff(FilterIndex index); void calcTable(double ratio, float*& table, unsigned& filterLen); struct Element { float* table; unsigned count; unsigned filterLen; }; std::map cache; }; ResampleCoeffs::ResampleCoeffs() { } ResampleCoeffs::~ResampleCoeffs() { assert(cache.empty()); } ResampleCoeffs& ResampleCoeffs::instance() { static ResampleCoeffs resampleCoeffs; return resampleCoeffs; } void ResampleCoeffs::getCoeffs( double ratio, float*& table, unsigned& filterLen) { auto it = cache.find(ratio); if (it != cache.end()) { it->second.count++; table = it->second.table; filterLen = it->second.filterLen; return; } calcTable(ratio, table, filterLen); Element elem; elem.count = 1; elem.table = table; elem.filterLen = filterLen; cache[ratio] = elem; } void ResampleCoeffs::releaseCoeffs(double ratio) { auto it = cache.find(ratio); assert(it != cache.end()); it->second.count--; if (it->second.count == 0) { MemoryOps::freeAligned(it->second.table); cache.erase(it); } } double ResampleCoeffs::getCoeff(FilterIndex index) { double fraction = index.fractionAsDouble(); int indx = index.toInt(); return double(coeffs[indx]) + fraction * (double(coeffs[indx + 1]) - double(coeffs[indx])); } void ResampleCoeffs::calcTable( double ratio, float*& table, unsigned& filterLen) { double floatIncr = (ratio > 1.0) ? INDEX_INC / ratio : INDEX_INC; double normFactor = floatIncr / INDEX_INC; FilterIndex increment = FilterIndex(floatIncr); FilterIndex maxFilterIndex(COEFF_HALF_LEN); int min_idx = -maxFilterIndex.divAsInt(increment); int max_idx = 1 + (maxFilterIndex - (increment - FilterIndex(floatIncr))).divAsInt(increment); int idx_cnt = max_idx - min_idx + 1; filterLen = (idx_cnt + 3) & ~3; // round up to multiple of 4 min_idx -= (filterLen - idx_cnt); table = static_cast(MemoryOps::mallocAligned( 16, TAB_LEN * filterLen * sizeof(float))); memset(table, 0, TAB_LEN * filterLen * sizeof(float)); for (unsigned t = 0; t < TAB_LEN; ++t) { double lastPos = double(t) / TAB_LEN; FilterIndex startFilterIndex(lastPos * floatIncr); FilterIndex filterIndex(startFilterIndex); int coeffCount = (maxFilterIndex - filterIndex).divAsInt(increment); filterIndex += increment * coeffCount; int bufIndex = -coeffCount; do { table[t * filterLen + bufIndex - min_idx] = float(getCoeff(filterIndex) * normFactor); filterIndex -= increment; bufIndex += 1; } while (filterIndex >= FilterIndex(0)); filterIndex = increment - startFilterIndex; coeffCount = (maxFilterIndex - filterIndex).divAsInt(increment); filterIndex += increment * coeffCount; bufIndex = 1 + coeffCount; do { table[t * filterLen + bufIndex - min_idx] = float(getCoeff(filterIndex) * normFactor); filterIndex -= increment; bufIndex -= 1; } while (filterIndex > FilterIndex(0)); } } template ResampleHQ::ResampleHQ( ResampledSoundDevice& input_, const DynamicClock& hostClock_, unsigned emuSampleRate) : input(input_) , hostClock(hostClock_) , emuClock(hostClock.getTime(), emuSampleRate) , ratio(float(emuSampleRate) / hostClock.getFreq()) { ResampleCoeffs::instance().getCoeffs(ratio, table, filterLen); // fill buffer with 'enough' zero's unsigned extra = int(filterLen + 1 + ratio + 1); bufStart = 0; bufEnd = extra; nonzeroSamples = 0; unsigned initialSize = 4000; // buffer grows dynamically if this is too small buffer.resize((initialSize + extra) * CHANNELS); // zero-initialized } template ResampleHQ::~ResampleHQ() { ResampleCoeffs::instance().releaseCoeffs(ratio); } #ifdef __SSE2__ static inline void calcSseMono(const float* buf_, const float* tab_, long len, int* out) { assert((len % 4) == 0); assert((long(tab_) % 16) == 0); long x = (len & ~7) * sizeof(float); assert((x % 32) == 0); const char* buf = reinterpret_cast(buf_) + x; const char* tab = reinterpret_cast(tab_) + x; x = -x; __m128 a0 = _mm_setzero_ps(); __m128 a1 = _mm_setzero_ps(); do { __m128 b0 = _mm_loadu_ps(reinterpret_cast(buf + x + 0)); __m128 b1 = _mm_loadu_ps(reinterpret_cast(buf + x + 16)); __m128 t0 = _mm_load_ps (reinterpret_cast(tab + x + 0)); __m128 t1 = _mm_load_ps (reinterpret_cast(tab + x + 16)); __m128 m0 = _mm_mul_ps(b0, t0); __m128 m1 = _mm_mul_ps(b1, t1); a0 = _mm_add_ps(a0, m0); a1 = _mm_add_ps(a1, m1); x += 2 * sizeof(__m128); } while (x < 0); if (len & 4) { __m128 b0 = _mm_loadu_ps(reinterpret_cast(buf)); __m128 t0 = _mm_load_ps (reinterpret_cast(tab)); __m128 m0 = _mm_mul_ps(b0, t0); a0 = _mm_add_ps(a0, m0); } __m128 a = _mm_add_ps(a0, a1); // The follwoing can _slighly_ faster by using the SSE3 _mm_hadd_ps() // intrinsic, but not worth the trouble. __m128 t = _mm_add_ps(a, _mm_movehl_ps(a, a)); __m128 s = _mm_add_ss(t, _mm_shuffle_ps(t, t, 1)); *out = _mm_cvtss_si32(s); } template static inline __m128 shuffle(__m128 x) { return _mm_castsi128_ps(_mm_shuffle_epi32(_mm_castps_si128(x), N)); } static inline void calcSseStereo(const float* buf_, const float* tab_, long len, int* out) { assert((len % 4) == 0); assert((long(tab_) % 16) == 0); long x = (len & ~7) * sizeof(float); const char* buf = reinterpret_cast(buf_) + 2*x; const char* tab = reinterpret_cast(tab_) + x; x = -x; __m128 a0 = _mm_setzero_ps(); __m128 a1 = _mm_setzero_ps(); __m128 a2 = _mm_setzero_ps(); __m128 a3 = _mm_setzero_ps(); do { __m128 b0 = _mm_loadu_ps(reinterpret_cast(buf + 2*x + 0)); __m128 b1 = _mm_loadu_ps(reinterpret_cast(buf + 2*x + 16)); __m128 b2 = _mm_loadu_ps(reinterpret_cast(buf + 2*x + 32)); __m128 b3 = _mm_loadu_ps(reinterpret_cast(buf + 2*x + 48)); __m128 ta = _mm_load_ps (reinterpret_cast(tab + x + 0)); __m128 tb = _mm_load_ps (reinterpret_cast(tab + x + 16)); __m128 t0 = shuffle<0x50>(ta); __m128 t1 = shuffle<0xFA>(ta); __m128 t2 = shuffle<0x50>(tb); __m128 t3 = shuffle<0xFA>(tb); __m128 m0 = _mm_mul_ps(b0, t0); __m128 m1 = _mm_mul_ps(b1, t1); __m128 m2 = _mm_mul_ps(b2, t2); __m128 m3 = _mm_mul_ps(b3, t3); a0 = _mm_add_ps(a0, m0); a1 = _mm_add_ps(a1, m1); a2 = _mm_add_ps(a2, m2); a3 = _mm_add_ps(a3, m3); x += 2 * sizeof(__m128); } while (x < 0); if (len & 4) { __m128 b0 = _mm_loadu_ps(reinterpret_cast(buf + 0)); __m128 b1 = _mm_loadu_ps(reinterpret_cast(buf + 16)); __m128 ta = _mm_load_ps (reinterpret_cast(tab)); __m128 t0 = shuffle<0x50>(ta); __m128 t1 = shuffle<0xFA>(ta); __m128 m0 = _mm_mul_ps(b0, t0); __m128 m1 = _mm_mul_ps(b1, t1); a0 = _mm_add_ps(a0, m0); a1 = _mm_add_ps(a1, m1); } __m128 a01 = _mm_add_ps(a0, a1); __m128 a23 = _mm_add_ps(a2, a3); __m128 a = _mm_add_ps(a01, a23); // Can faster with SSE3, but (like above) not worth the trouble. __m128 s = _mm_add_ps(a, _mm_movehl_ps(a, a)); __m128i si = _mm_cvtps_epi32(s); #if ASM_X86_64 *reinterpret_cast(out) = _mm_cvtsi128_si64(si); #else out[0] = _mm_cvtsi128_si32(si); out[1] = _mm_cvtsi128_si32(_mm_shuffle_epi32(si, 0x55)); #endif } #endif template void ResampleHQ::calcOutput( float pos, int* __restrict output) { assert((filterLen & 3) == 0); int t = int(pos * TAB_LEN + 0.5f) % TAB_LEN; const float* tab = &table[t * filterLen]; int bufIdx = int(pos) + bufStart; assert((bufIdx + filterLen) <= bufEnd); bufIdx *= CHANNELS; const float* buf = &buffer[bufIdx]; #ifdef __SSE2__ if (CHANNELS == 1) { calcSseMono (buf, tab, filterLen, output); } else { calcSseStereo(buf, tab, filterLen, output); } return; #endif // c++ version, both mono and stereo for (unsigned ch = 0; ch < CHANNELS; ++ch) { float r0 = 0.0f; float r1 = 0.0f; float r2 = 0.0f; float r3 = 0.0f; for (unsigned i = 0; i < filterLen; i += 4) { r0 += tab[i + 0] * buf[CHANNELS * (i + 0)]; r1 += tab[i + 1] * buf[CHANNELS * (i + 1)]; r2 += tab[i + 2] * buf[CHANNELS * (i + 2)]; r3 += tab[i + 3] * buf[CHANNELS * (i + 3)]; } output[ch] = lrint(r0 + r1 + r2 + r3); ++buf; } } template void ResampleHQ::prepareData(unsigned emuNum) { // Still enough free space at end of buffer? unsigned free = unsigned(buffer.size() / CHANNELS) - bufEnd; if (free < emuNum) { // No, then move everything to the start // (data needs to be in a contiguous memory block) unsigned available = bufEnd - bufStart; memmove(&buffer[0], &buffer[bufStart * CHANNELS], available * CHANNELS * sizeof(float)); bufStart = 0; bufEnd = available; free = unsigned(buffer.size() / CHANNELS) - bufEnd; int missing = emuNum - free; if (unlikely(missing > 0)) { // Still not enough room: grow the buffer. // TODO an alternative is to instead of using a large // buffer, chop the work in multiple smaller pieces. // That may have the advantage that the data fits in // the CPU's data cache. OTOH too small chunks have // more overhead. (Not yet implemented because it's // more complex). buffer.resize(buffer.size() + missing * CHANNELS); } } VLA_SSE_ALIGNED(int, tmpBuf, emuNum * CHANNELS + 3); if (input.generateInput(tmpBuf, emuNum)) { for (unsigned i = 0; i < emuNum * CHANNELS; ++i) { buffer[bufEnd * CHANNELS + i] = float(tmpBuf[i]); } bufEnd += emuNum; nonzeroSamples = bufEnd - bufStart; } else { memset(&buffer[bufEnd * CHANNELS], 0, emuNum * CHANNELS * sizeof(float)); bufEnd += emuNum; } assert(bufStart <= bufEnd); assert(bufEnd <= (buffer.size() / CHANNELS)); } template bool ResampleHQ::generateOutput( int* __restrict dataOut, unsigned hostNum, EmuTime::param time) { unsigned emuNum = emuClock.getTicksTill(time); if (emuNum > 0) { prepareData(emuNum); } bool notMuted = nonzeroSamples > 0; if (notMuted) { // main processing loop EmuTime host1 = hostClock.getFastAdd(1); assert(host1 > emuClock.getTime()); float pos = emuClock.getTicksTillDouble(host1); assert(pos <= (ratio + 2)); for (unsigned i = 0; i < hostNum; ++i) { calcOutput(pos, &dataOut[i * CHANNELS]); pos += ratio; } } emuClock += emuNum; bufStart += emuNum; nonzeroSamples = std::max(0, nonzeroSamples - emuNum); assert(bufStart <= bufEnd); unsigned available = bufEnd - bufStart; unsigned extra = int(filterLen + 1 + ratio + 1); assert(available == extra); (void)available; (void)extra; return notMuted; } // Force template instantiation. template class ResampleHQ<1>; template class ResampleHQ<2>; } // namespace openmsx openmsx-0.10.0/src/sound/EmuTimer.cc0000644000175000017500000000442112262345041020022 0ustar manuelmanuel00000000000000#include "EmuTimer.hh" #include "serialize.hh" #include "memory.hh" using std::unique_ptr; namespace openmsx { unique_ptr EmuTimer::createOPM_1( Scheduler& scheduler, EmuTimerCallback& cb) { return make_unique( scheduler, cb, 0x40, 3579545, 64 * 2 , 1024); } unique_ptr EmuTimer::createOPM_2( Scheduler& scheduler, EmuTimerCallback& cb) { return make_unique( scheduler, cb, 0x20, 3579545, 64 * 2 * 16, 256); } unique_ptr EmuTimer::createOPL3_1( Scheduler& scheduler, EmuTimerCallback& cb) { return make_unique( scheduler, cb, 0x40, 3579545, 72 * 4 , 256); } unique_ptr EmuTimer::createOPL3_2( Scheduler& scheduler, EmuTimerCallback& cb) { return make_unique( scheduler, cb, 0x20, 3579545, 72 * 4 * 4, 256); } unique_ptr EmuTimer::createOPL4_1( Scheduler& scheduler, EmuTimerCallback& cb) { return make_unique( scheduler, cb, 0x40, 33868800, 72 * 38 , 256); } unique_ptr EmuTimer::createOPL4_2( Scheduler& scheduler, EmuTimerCallback& cb) { return make_unique( scheduler, cb, 0x20, 33868800, 72 * 38 * 4, 256); } EmuTimer::EmuTimer(Scheduler& scheduler, EmuTimerCallback& cb_, byte flag_, unsigned freq_num, unsigned freq_denom, unsigned maxval_) : Schedulable(scheduler), cb(cb_) , clock(EmuTime::dummy()) , maxval(maxval_), count(maxval_) , flag(flag_), counting(false) { clock.setFreq(freq_num, freq_denom); } void EmuTimer::setValue(int value) { count = maxval - value; } void EmuTimer::setStart(bool start, EmuTime::param time) { if (start != counting) { counting = start; if (start) { schedule(time); } else { unschedule(); } } } void EmuTimer::schedule(EmuTime::param time) { clock.reset(time); clock += count; setSyncPoint(clock.getTime()); } void EmuTimer::unschedule() { removeSyncPoint(); } void EmuTimer::executeUntil(EmuTime::param time, int /*userData*/) { cb.callback(flag); schedule(time); } template void EmuTimer::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("count", count); ar.serialize("counting", counting); } INSTANTIATE_SERIALIZE_METHODS(EmuTimer); } // namespace openmsx openmsx-0.10.0/src/sound/MSXPSG.cc0000644000175000017500000000637712262345041017330 0ustar manuelmanuel00000000000000#include "MSXPSG.hh" #include "AY8910.hh" #include "LedStatus.hh" #include "CassettePort.hh" #include "MSXMotherBoard.hh" #include "JoystickPort.hh" #include "RenShaTurbo.hh" #include "serialize.hh" #include "checked_cast.hh" #include "memory.hh" namespace openmsx { // MSXDevice MSXPSG::MSXPSG(const DeviceConfig& config) : MSXDevice(config) , cassette(getMotherBoard().getCassettePort()) , renShaTurbo(getMotherBoard().getRenShaTurbo()) , prev(255) , keyLayoutBit(config.getChildData("keyboardlayout", "") == "JIS") { selectedPort = 0; ports[0] = &getMotherBoard().getJoystickPort(0); ports[1] = &getMotherBoard().getJoystickPort(1); // must come after initialisation of ports EmuTime::param time = getCurrentTime(); ay8910 = make_unique("PSG", *this, config, time); reset(time); } MSXPSG::~MSXPSG() { powerDown(EmuTime::dummy()); } void MSXPSG::reset(EmuTime::param time) { registerLatch = 0; ay8910->reset(time); } void MSXPSG::powerDown(EmuTime::param /*time*/) { getLedStatus().setLed(LedStatus::KANA, false); } byte MSXPSG::readIO(word /*port*/, EmuTime::param time) { byte result = ay8910->readRegister(registerLatch, time); //PRT_DEBUG("PSG read R#"<<(int)registerLatch<<" = "<<(int)result); return result; } byte MSXPSG::peekIO(word /*port*/, EmuTime::param time) const { return ay8910->peekRegister(registerLatch, time); } void MSXPSG::writeIO(word port, byte value, EmuTime::param time) { switch (port & 0x03) { case 0: registerLatch = value & 0x0F; break; case 1: //PRT_DEBUG("PSG write R#"<<(int)registerLatch<<" = "<<(int)value); ay8910->writeRegister(registerLatch, value, time); break; } } // AY8910Periphery byte MSXPSG::readA(EmuTime::param time) { byte joystick = ports[selectedPort]->read(time) | ((renShaTurbo.getSignal(time)) ? 0x10 : 0x00); // pin 6,7 input is ANDed with pin 6,7 output byte pin67 = prev << (4 - 2 * selectedPort); joystick &= (pin67| 0xCF); byte cassetteInput = cassette.cassetteIn(time) ? 0x80 : 0x00; byte keyLayout = keyLayoutBit ? 0x40 : 0x00; return joystick | keyLayout | cassetteInput; } void MSXPSG::writeB(byte value, EmuTime::param time) { byte val0 = (value & 0x03) | ((value & 0x10) >> 2); byte val1 = ((value & 0x0C) >> 2) | ((value & 0x20) >> 3); ports[0]->write(val0, time); ports[1]->write(val1, time); selectedPort = (value & 0x40) >> 6; if ((prev ^ value) & 0x80) { getLedStatus().setLed(LedStatus::KANA, !(value & 0x80)); } prev = value; } // version 1: initial version // version 2: joystickportA/B moved from here to MSXMotherBoard template void MSXPSG::serialize(Archive& ar, unsigned version) { ar.template serializeBase(*this); ar.serialize("ay8910", *ay8910); if (ar.versionBelow(version, 2)) { assert(ar.isLoader()); // in older versions there were always 2 real joytsick ports ar.serialize("joystickportA", *checked_cast(ports[0])); ar.serialize("joystickportB", *checked_cast(ports[1])); } ar.serialize("registerLatch", registerLatch); byte portB = prev; ar.serialize("portB", portB); if (ar.isLoader()) { writeB(portB, getCurrentTime()); } // selectedPort is derived from portB } INSTANTIATE_SERIALIZE_METHODS(MSXPSG); REGISTER_MSXDEVICE(MSXPSG, "PSG"); } // namespace openmsx openmsx-0.10.0/src/sound/YM2413Core.hh0000644000175000017500000001152712262345041017762 0ustar manuelmanuel00000000000000#ifndef YM2413CORE_HH #define YM2413CORE_HH #include "openmsx.hh" namespace openmsx { /** Abstract interface for the YM2413 core. * * We currently have two concrete implementations: * - YM2413Okazaki * - YM2413Burczynski * The long term goal is to study the difference between these two * implementations and merge the best parts of either core into a single core. * * This interface separates the actual YM2413 emulation from the rest of the * (sound) emulation details. This allows to more easily share this * implementation between different emulators. It also allows to more easily * test the YM2413 emulation in isolation. * * There are two main functions in this interface: write to registers and get * output samples. All timing information is implicit in the order of the calls * to these functions (e.g. write some register, generate 10 output samples, * write another register, ...). */ class YM2413Core { public: /** Input clock frequency. * An output sample is generated every 72 cycles. So the output sample * frequency is effectively 49716Hz. If you need the output at e.g. * 44100Hz you need to resample the data. Generating the output at the * native YM2413 frequency allows for quite some simplifications in the * implementation of the cores. It also generally results in better * sound quality. At least if the resampler step itself doesn't degrade * the quality again (Certainly don't simply skip some of the samples * to get to 44kHz, at the very least do linear interpolation. But * preferably use a better resample algorithm). */ static const int CLOCK_FREQ = 3579545; virtual ~YM2413Core() {} /** Reset this YM2413 core. */ virtual void reset() = 0; /** Write to a YM2413 register. */ virtual void writeReg(byte reg, byte value) = 0; /** Read from a YM2413 register. * Note that the real YM2413 chip doesn't allow to read the registers. * This returns the last written value or the default value if this * register hasn't been written to since the last reset() call. * Reading registers has no influence on the generated sound. */ virtual byte peekReg(byte reg) const = 0; /** Generate the sound output. * @param bufs Pointers to output buffers. * @param num The number of required output samples. * * The requested number of samples must be strictly bigger than zero. * * The output of the different channels is put in separate output * buffers. This makes it possible to e.g. record individual channels * or to pan, mute or adjust volume per channel. The YM2413 can operate * in two modes: 9 channels or 6 channels + 5 drum channels. The latter * mode requires a total of 11 output buffers. In the first mode, the * last two output buffers are filled with silence (this is very * efficient, see below). Each output buffer should be big enough to * hold at least 'num' number of ints. * * The output is not simply stored in the buffer, but added to the * existing data in the buffer. So you'll have to zero the content * of the buffer before passing it to this function. OTOH if you want * to combine (some of) the channels, you can pass the same buffer for * all those channels. This approach may have a little overhead when * you're interested in all channels, but it is very efficient if * you're only interested in the combined channels (the most common * case). You can even directly combine this output with other chips * with the same native frequency (like Y8950 or YMF262). * * When the core detects that some channel is silent, it will assign * nullptr to the buffer pointer (so the content of the buffer is left * unchanged, but the pointer to that buffer is set to zero). When all * the channels you're interested in are silent you can even skip all * subsequent audio processing for this channel (e.g. skip resampling to * 44kHz). It is very common that some or even all of the channels are * silent, so it's definitely worth it to implement this optimization. * Also the cores internally try to detect silent channels very early, * so an idle YM2413 core generally requires very little emulation * time. */ virtual void generateChannels(int* bufs[11], unsigned num) = 0; /** Returns normalization factor. * The output of the generateChannels() method should still be * amplified (=multiplied) with this factor to get a consistent volume * level across the different implementations of the YM2413 core. This * allows to internally calculate with native volume levels, and * possibly results in slightly simpler and/or faster code. It's very * likely that subsequent audio processing steps (like resampler, * filters or volume adjustments) must anyway still multiply the output * sample values, so this factor can be folded-in for free. */ virtual int getAmplificationFactor() const = 0; protected: YM2413Core() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/Y8950Periphery.cc0000644000175000017500000002151012262345041020717 0ustar manuelmanuel00000000000000#include "Y8950Periphery.hh" #include "Y8950.hh" #include "MSXAudio.hh" #include "MSXCPU.hh" #include "MSXCPUInterface.hh" #include "MSXDevice.hh" #include "CacheLine.hh" #include "Ram.hh" #include "Rom.hh" #include "BooleanSetting.hh" #include "CommandController.hh" #include "MSXException.hh" #include "StringOp.hh" #include "DeviceConfig.hh" #include "serialize.hh" #include "memory.hh" #include using std::string; namespace openmsx { // Subclass declarations: class MusicModulePeriphery : public Y8950Periphery { public: explicit MusicModulePeriphery(MSXAudio& audio); virtual void write(nibble outputs, nibble values, EmuTime::param time); virtual nibble read(EmuTime::param time); template void serialize(Archive& /*ar*/, unsigned /*version*/) { // nothing } private: MSXAudio& audio; }; REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, MusicModulePeriphery, "MusicModule"); class PanasonicAudioPeriphery : public Y8950Periphery { public: PanasonicAudioPeriphery( MSXAudio& audio, const DeviceConfig& config, const string& soundDeviceName); ~PanasonicAudioPeriphery(); virtual void reset(); virtual void write(nibble outputs, nibble values, EmuTime::param time); virtual nibble read(EmuTime::param time); virtual byte peekMem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte* getWriteCacheLine(word start) const; template void serialize(Archive& ar, unsigned version); private: void setBank(byte value); void setIOPorts(byte value); void setIOPortsHelper(unsigned base, bool enable); MSXAudio& audio; BooleanSetting swSwitch; const std::unique_ptr ram; const std::unique_ptr rom; byte bankSelect; byte ioPorts; }; REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, PanasonicAudioPeriphery, "Panasonic"); class ToshibaAudioPeriphery : public Y8950Periphery { public: explicit ToshibaAudioPeriphery(MSXAudio& audio); virtual void write(nibble outputs, nibble values, EmuTime::param time); virtual nibble read(EmuTime::param time); virtual void setSPOFF(bool value, EmuTime::param time); template void serialize(Archive& /*ar*/, unsigned /*version*/) { // nothing } private: MSXAudio& audio; }; REGISTER_POLYMORPHIC_INITIALIZER(Y8950Periphery, ToshibaAudioPeriphery, "Toshiba"); // Base class implementation: Y8950Periphery::~Y8950Periphery() { } void Y8950Periphery::reset() { // nothing } void Y8950Periphery::setSPOFF(bool /*value*/, EmuTime::param /*time*/) { // nothing } byte Y8950Periphery::readMem(word address, EmuTime::param time) { // by default do same as peekMem() return peekMem(address, time); } byte Y8950Periphery::peekMem(word /*address*/, EmuTime::param /*time*/) const { return 0xFF; } void Y8950Periphery::writeMem(word /*address*/, byte /*value*/, EmuTime::param /*time*/) { // nothing } const byte* Y8950Periphery::getReadCacheLine(word /*start*/) const { return MSXDevice::unmappedRead; } byte* Y8950Periphery::getWriteCacheLine(word /*start*/) const { return MSXDevice::unmappedWrite; } // MusicModulePeriphery implementation: MusicModulePeriphery::MusicModulePeriphery(MSXAudio& audio_) : audio(audio_) { } void MusicModulePeriphery::write(nibble outputs, nibble values, EmuTime::param time) { nibble actual = (outputs & values) | (~outputs & read(time)); audio.y8950->setEnabled((actual & 8) != 0, time); audio.enableDAC((actual & 1) != 0, time); } nibble MusicModulePeriphery::read(EmuTime::param /*time*/) { // IO2-IO1 are unconnected, reading them initially returns the last // written value, but after some seconds it falls back to '0' // IO3 and IO0 are output pins, but reading them return respectively // '1' and '0' return 8; } // PanasonicAudioPeriphery implementation: PanasonicAudioPeriphery::PanasonicAudioPeriphery( MSXAudio& audio_, const DeviceConfig& config, const string& soundDeviceName) : audio(audio_) , swSwitch(audio.getCommandController(), soundDeviceName + "_firmware", "This setting controls the switch on the Panasonic " "MSX-AUDIO module. The switch controls whether the internal " "software of this module must be started or not.", false) // note: name + " RAM" already taken by sample RAM , ram(make_unique( config, audio.getName() + " mapped RAM", "MSX-AUDIO mapped RAM", 0x1000)) , rom(make_unique( audio.getName() + " ROM", "MSX-AUDIO ROM", config)) , ioPorts(0) { reset(); } PanasonicAudioPeriphery::~PanasonicAudioPeriphery() { setIOPorts(0); // unregister IO ports } void PanasonicAudioPeriphery::reset() { ram->clear(); // TODO check setBank(0); setIOPorts(0); // TODO check: neither IO port ranges active } void PanasonicAudioPeriphery::write(nibble outputs, nibble values, EmuTime::param time) { nibble actual = (outputs & values) | (~outputs & read(time)); audio.y8950->setEnabled(!(actual & 8), time); } nibble PanasonicAudioPeriphery::read(EmuTime::param /*time*/) { // verified bit 0,1,3 read as zero return swSwitch.getBoolean() ? 0x4 : 0x0; // bit2 } byte PanasonicAudioPeriphery::peekMem(word address, EmuTime::param /*time*/) const { if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) { return (*ram)[(address & 0x3FFF) - 0x3000]; } else { return (*rom)[0x8000 * bankSelect + (address & 0x7FFF)]; } } const byte* PanasonicAudioPeriphery::getReadCacheLine(word address) const { if ((bankSelect == 0) && ((address & 0x3FFF) >= 0x3000)) { return &(*ram)[(address & 0x3FFF) - 0x3000]; } else { return &(*rom)[0x8000 * bankSelect + (address & 0x7FFF)]; } } void PanasonicAudioPeriphery::writeMem(word address, byte value, EmuTime::param /*time*/) { address &= 0x7FFF; if (address == 0x7FFE) { setBank(value); } else if (address == 0x7FFF) { setIOPorts(value); } address &= 0x3FFF; if ((bankSelect == 0) && (address >= 0x3000)) { (*ram)[address - 0x3000] = value; } } byte* PanasonicAudioPeriphery::getWriteCacheLine(word address) const { address &= 0x7FFF; if (address == (0x7FFE & CacheLine::HIGH)) { return nullptr; } address &= 0x3FFF; if ((bankSelect == 0) && (address >= 0x3000)) { return const_cast(&(*ram)[address - 0x3000]); } else { return MSXDevice::unmappedWrite; } } void PanasonicAudioPeriphery::setBank(byte value) { bankSelect = value & 3; audio.getCPU().invalidateMemCache(0x0000, 0x10000); } void PanasonicAudioPeriphery::setIOPorts(byte value) { byte diff = ioPorts ^ value; if (diff & 1) { setIOPortsHelper(0xC0, (value & 1) != 0); } if (diff & 2) { setIOPortsHelper(0xC2, (value & 2) != 0); } ioPorts = value; } void PanasonicAudioPeriphery::setIOPortsHelper(unsigned base, bool enable) { MSXCPUInterface& cpu = audio.getCPUInterface(); if (enable) { cpu.register_IO_In (base + 0, &audio); cpu.register_IO_In (base + 1, &audio); cpu.register_IO_Out(base + 0, &audio); cpu.register_IO_Out(base + 1, &audio); } else { cpu.unregister_IO_In (base + 0, &audio); cpu.unregister_IO_In (base + 1, &audio); cpu.unregister_IO_Out(base + 0, &audio); cpu.unregister_IO_Out(base + 1, &audio); } } template void PanasonicAudioPeriphery::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("ram", *ram); ar.serialize("bankSelect", bankSelect); byte tmpIoPorts = ioPorts; ar.serialize("ioPorts", tmpIoPorts); if (ar.isLoader()) { setIOPorts(tmpIoPorts); } } // ToshibaAudioPeriphery implementation: ToshibaAudioPeriphery::ToshibaAudioPeriphery(MSXAudio& audio_) : audio(audio_) { } void ToshibaAudioPeriphery::write(nibble /*outputs*/, nibble /*values*/, EmuTime::param /*time*/) { // TODO IO1-IO0 are programmed as output by HX-MU900 software rom // and it writes periodically the values 1/1/2/2/0/0 to // these pins, but I have no idea what function they have } nibble ToshibaAudioPeriphery::read(EmuTime::param /*time*/) { // IO3-IO2 are unconnected (see also comment in MusicModulePeriphery) // IO1-IO0 are output pins, but reading them returns '1' return 0x3; } void ToshibaAudioPeriphery::setSPOFF(bool value, EmuTime::param time) { audio.y8950->setEnabled(!value, time); } // Y8950PeripheryFactory implementation: std::unique_ptr Y8950PeripheryFactory::create( MSXAudio& audio, const DeviceConfig& config, const std::string& soundDeviceName) { string type(StringOp::toLower(config.getChildData("type", "philips"))); if (type == "philips") { return make_unique(audio); } else if (type == "panasonic") { return make_unique( audio, config, soundDeviceName); } else if (type == "toshiba") { return make_unique(audio); } else { throw MSXException("Unknown MSX-AUDIO type: " + type); } } } // namespace openmsx openmsx-0.10.0/src/sound/DummyAudioInputDevice.hh0000644000175000017500000000065512262345041022527 0ustar manuelmanuel00000000000000#ifndef DUMMYAUDIOINPUTDEVICE_HH #define DUMMYAUDIOINPUTDEVICE_HH #include "AudioInputDevice.hh" namespace openmsx { class DummyAudioInputDevice : public AudioInputDevice { public: virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); virtual short readSample(EmuTime::param time); }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXOPL3Cartridge.cc0000644000175000017500000000366312262345041021234 0ustar manuelmanuel00000000000000#include "MSXOPL3Cartridge.hh" #include "YMF262.hh" #include "serialize.hh" #include "unreachable.hh" #include "memory.hh" namespace openmsx { MSXOPL3Cartridge::MSXOPL3Cartridge(const DeviceConfig& config) : MSXDevice(config) , ymf262(make_unique(getName(), config, false)) { reset(getCurrentTime()); } MSXOPL3Cartridge::~MSXOPL3Cartridge() { } void MSXOPL3Cartridge::reset(EmuTime::param time) { ymf262->reset(time); // TODO check opl3latch = 0; } byte MSXOPL3Cartridge::readIO(word port, EmuTime::param /*time*/) { byte result; // FM part 0xC4-0xC7 (in MoonSound) switch (port & 0x03) { case 0: // read status case 2: result = ymf262->readStatus(); break; case 1: case 3: // read fm register result = ymf262->readReg(opl3latch); break; default: // unreachable, avoid warning UNREACHABLE; result = 255; } return result; } byte MSXOPL3Cartridge::peekIO(word port, EmuTime::param /*time*/) const { byte result; switch (port & 0x03) { case 0: // read status case 2: result = ymf262->peekStatus(); break; case 1: case 3: // read fm register result = ymf262->peekReg(opl3latch); break; default: // unreachable, avoid warning UNREACHABLE; result = 255; } return result; } void MSXOPL3Cartridge::writeIO(word port, byte value, EmuTime::param time) { switch (port & 0x03) { case 0: // select register bank 0 opl3latch = value; break; case 2: // select register bank 1 opl3latch = value | 0x100; break; case 1: case 3: // write fm register ymf262->writeReg(opl3latch, value, time); break; default: UNREACHABLE; } } template void MSXOPL3Cartridge::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("ymf262", *ymf262); ar.serialize("opl3latch", opl3latch); } INSTANTIATE_SERIALIZE_METHODS(MSXOPL3Cartridge); REGISTER_MSXDEVICE(MSXOPL3Cartridge, "OPL3Cartridge"); } // namespace openmsx openmsx-0.10.0/src/sound/NullSoundDriver.hh0000644000175000017500000000056612262345041021412 0ustar manuelmanuel00000000000000#ifndef NULLSOUNDDRIVER_HH #define NULLSOUNDDRIVER_HH #include "SoundDriver.hh" namespace openmsx { class NullSoundDriver : public SoundDriver { public: virtual void mute(); virtual void unmute(); virtual unsigned getFrequency() const; virtual unsigned getSamples() const; virtual void uploadBuffer(short* buffer, unsigned len); }; } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXMusic.hh0000644000175000017500000000155412262345041017761 0ustar manuelmanuel00000000000000#ifndef MSXMUSIC_HH #define MSXMUSIC_HH #include "MSXDevice.hh" #include "serialize_meta.hh" #include namespace openmsx { class Rom; class YM2413; class MSXMusic : public MSXDevice { public: explicit MSXMusic(const DeviceConfig& config); virtual ~MSXMusic(); virtual void reset(EmuTime::param time); virtual void writeIO(word port, byte value, EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; template void serialize(Archive& ar, unsigned version); protected: void writeRegisterPort(byte value, EmuTime::param time); void writeDataPort(byte value, EmuTime::param time); const std::unique_ptr rom; const std::unique_ptr ym2413; private: int registerLatch; }; SERIALIZE_CLASS_VERSION(MSXMusic, 2); } // namespace openmsx #endif openmsx-0.10.0/src/sound/MSXMixer.hh0000644000175000017500000001151612262345041017764 0ustar manuelmanuel00000000000000#ifndef MSXMIXER_HH #define MSXMIXER_HH #include "Schedulable.hh" #include "Observer.hh" #include "EmuTime.hh" #include "DynamicClock.hh" #include #include #include namespace openmsx { class SoundDevice; class Mixer; class Scheduler; class MSXCommandController; class CommandController; class GlobalSettings; class ThrottleManager; class IntegerSetting; class StringSetting; class BooleanSetting; class Setting; class SoundDeviceInfoTopic; class AviRecorder; class MSXMixer : private Schedulable, private Observer , private Observer { public: MSXMixer(Mixer& mixer, Scheduler& scheduler, MSXCommandController& msxCommandController, GlobalSettings& globalSettings); virtual ~MSXMixer(); /** * Use this method to register a given sounddevice. * * While registering, the device its setSampleRate() method is * called (see SoundDevice for more info). * After registration the device its updateBuffer() method is * 'regularly' called (see SoundDevice for more info). */ void registerSound(SoundDevice& device, double volume, int balance, unsigned numChannels); /** * Every sounddevice must unregister before it is destructed */ void unregisterSound(SoundDevice& device); /** * Use this method to force an 'early' call to all * updateBuffer() methods. */ void updateStream(EmuTime::param time); /** Returns the ratio of emutime-speed per realtime-speed. * In other words how many times faster emutime goes compared to * realtime. This depends on the 'speed' setting but also on whether * we're recording or not (in case of recording we want to generate * sound as if realtime and emutime go at the same speed. */ double getEffectiveSpeed() const; /** If we're recording, we want to emulate sound at 100% emutime speed. * See alsoe getEffectiveSpeed(). */ void setSynchronousMode(bool synchronous); /** TODO * This methods (un)mute the sound. * These methods may be called multiple times, as long as * you never call unmute() more than mute() */ void mute(); void unmute(); // Called by Mixer or SoundDriver /** Set new fragment size and sample frequency. * A fragment size of zero means the Mixer is muted. */ void setMixerParams(unsigned fragmentSize, unsigned sampleRate); /** Clock that ticks at the exact moment(s) in time that a host sample * should be generated. The current time of this clock is the time of * the last generated sample. The rate of this clock is the same as * the host sample rate. * Note that this rate is not the same as the frequency set with the * 'frequency' setting. Either because the sound driver can't handle * the requested speed or because the 'speed' setting is different * from 100. */ const DynamicClock& getHostSampleClock() const; // Called by AviRecorder bool needStereoRecording() const; void setRecorder(AviRecorder* recorder); // Returns the nominal host sample rate (not adjusted for speed setting) unsigned getSampleRate() const; SoundDevice* findDevice(string_ref name) const; void reInit(); private: struct SoundDeviceInfo { SoundDeviceInfo(); SoundDeviceInfo(SoundDeviceInfo&& rhs); SoundDeviceInfo& operator=(SoundDeviceInfo&& rhs); double defaultVolume; std::unique_ptr volumeSetting; std::unique_ptr balanceSetting; struct ChannelSettings { ChannelSettings(); ChannelSettings(ChannelSettings&& rhs); ChannelSettings& operator=(ChannelSettings&& rhs); std::unique_ptr recordSetting; std::unique_ptr muteSetting; }; std::vector channelSettings; int left1, right1, left2, right2; }; typedef std::map Infos; void updateVolumeParams(Infos::value_type& p); void updateMasterVolume(); void reschedule(); void reschedule2(); void generate(short* buffer, EmuTime::param time, unsigned samples); // Schedulable void executeUntil(EmuTime::param time, int userData); // Observer virtual void update(const Setting& setting); // Observer virtual void update(const ThrottleManager& throttleManager); void changeRecordSetting(const Setting& setting); void changeMuteSetting(const Setting& setting); unsigned fragmentSize; unsigned hostSampleRate; // requested freq by sound driver, // not compensated for speed Infos infos; Mixer& mixer; CommandController& commandController; IntegerSetting& masterVolume; IntegerSetting& speedSetting; ThrottleManager& throttleManager; DynamicClock prevTime; friend class SoundDeviceInfoTopic; const std::unique_ptr soundDeviceInfo; AviRecorder* recorder; unsigned synchronousCounter; unsigned muteCount; int prevLeft, prevRight; int outLeft, outRight; }; } // namespace openmsx #endif openmsx-0.10.0/src/GlobalSettings.hh0000644000175000017500000000406312262345041020100 0ustar manuelmanuel00000000000000#ifndef GLOBALSETTINGS_HH #define GLOBALSETTINGS_HH #include "Observer.hh" #include "noncopyable.hh" #include "ResampledSoundDevice.hh" #include namespace openmsx { class GlobalCommandController; class IntegerSetting; class BooleanSetting; class StringSetting; class ThrottleManager; class Setting; template class EnumSetting; /** * This class contains settings that are used by several other class * (including some singletons). This class was introduced to solve * lifetime management issues. */ class GlobalSettings : private Observer, private noncopyable { public: explicit GlobalSettings(GlobalCommandController& commandController); ~GlobalSettings(); IntegerSetting& getSpeedSetting() const { return *speedSetting; } BooleanSetting& getPauseSetting() const { return *pauseSetting; } BooleanSetting& getPowerSetting() const { return *powerSetting; } BooleanSetting& getAutoSaveSetting() const { return *autoSaveSetting; } BooleanSetting& getPauseOnLostFocusSetting() const { return *pauseOnLostFocusSetting; } StringSetting& getUMRCallBackSetting() const { return *umrCallBackSetting; } StringSetting& getInvalidPsgDirectionsSetting() const { return *invalidPsgDirectionsSetting; } EnumSetting& getResampleSetting() const { return * resampleSetting; } ThrottleManager& getThrottleManager() const { return *throttleManager; } private: // Observer virtual void update(const Setting& setting); GlobalCommandController& commandController; std::unique_ptr speedSetting; std::unique_ptr pauseSetting; std::unique_ptr powerSetting; std::unique_ptr autoSaveSetting; std::unique_ptr pauseOnLostFocusSetting; std::unique_ptr umrCallBackSetting; std::unique_ptr invalidPsgDirectionsSetting; std::unique_ptr> resampleSetting; std::unique_ptr throttleManager; }; } // namespace openmsx #endif openmsx-0.10.0/src/resource/0000755000175000017500000000000012262345041016462 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/resource/node.mk0000644000175000017500000000013112262345041017733 0ustar manuelmanuel00000000000000include build/node-start.mk DIST:= \ openmsx.ico openmsx.rc include build/node-end.mk openmsx-0.10.0/src/resource/openmsx.rc0000644000175000017500000000677312262345041020516 0ustar manuelmanuel00000000000000// Originally a Microsoft Developer Studio generated resource script, // but now maintained manually because we want to build with MinGW32+MSYS. // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 103 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif // Include resource info from the openMSX build process. #include "resource-info.h" //#include "tk_base.rc" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. OPENMSX ICON DISCARDABLE "openmsx.ico" #ifndef _MAC ///////////////////////////////////////////////////////////////////////////// // // Version // VS_VERSION_INFO VERSIONINFO FILEVERSION OPENMSX_VERSION_INT PRODUCTVERSION OPENMSX_VERSION_INT FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x40004L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080004b0" BEGIN VALUE "Comments", "\0" VALUE "CompanyName", " \0" VALUE "FileDescription", "openMSX\0" VALUE "FileVersion", OPENMSX_VERSION_STR VALUE "InternalName", "openmsx\0" VALUE "LegalCopyright", "Copyright 2001-2014\0" VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "openmsx.exe\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "openMSX\0" VALUE "ProductVersion", OPENMSX_VERSION_STR VALUE "SpecialBuild", "\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x800, 1200 END END #endif // !_MAC #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // English (U.S.) (unknown sub-lang: 0xC) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, 0xC #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED #endif // English (U.S.) (unknown sub-lang: 0xC) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED openmsx-0.10.0/src/resource/openmsx.ico0000644000175000017500000014254612262345041020663 0ustar manuelmanuel00000000000000 (Æhî èV ¨> 00hæ00¨N@@h ö'@@(^2 h†H  ¨îL00 ¨%–]@@ (B>ƒ( À€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ»;0»»ÿó°»»ÿÿÿ0»·ÿÿ»ÿÿÿÿ÷Dÿ÷DDÿÿ÷DO{wD±·@{@@÷ø@DDÿÿó‡ÀÀÀÀààààðøøøü?ÿÿ( @–Û©©ÿÿÿä½½Ãmm~~~{{{­ôøàî”ñömDÆä¢ßÄÄúþþÈË%âîŽÇÊÓÏÏfc÷ööïîîóññÕœœ2Ø¥¥JèÉÉ:zKÑêxæótåò¼¼¼œ¯§»%%**%)%%%%&)%%%%%)%%%'(%%& !"##$    ÿÿó‡ÀÀÀÀààààðøøøü?ÿÿ( @€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ33³0ˆ³»1ˆˆ‡÷ÿÿ÷;³;7ÿÿÿÿÿÿó³;»»ÿ÷ÿÿÿÿ÷0»»³÷ÿÿÿÿ÷;»»¸xÿÿÿÿÿø»»»·÷ÿÿÿÿÿô»»³?ÿÿÿÿÿÿø»»³ÿÿÿÿÿÿ÷;³7ÿÿÿˆ„DHø7ÿÿÿ÷DDDDDOÿÿÿøDDDD@ÿÿÿøDDDDDHÿÿÿøDDDDDDÿÿÿÿ„wDDDDÿÿÿÿÿôDDDOÿÿÿÿÿøD@DDÿÿÿÿÿøD@G÷wÿÿôDDƒ™?÷D@DDDC;»34@H·»¸@H÷»ÿ„@Gx·ô@Gwÿô@Gÿÿÿt@HÿwÿDDDDDDDÿÿü?ÿÿàÿüþüøððàààððüüøøø?ø?üÿÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿ€ÿÿÀÿÿÀÿÿÀÿÿàÿÿø?ÿ( @€      !%)08>DINRTX[^`bceimqvy|||{ywutt u soljikm n"#p%%r()s+,t..s01s45r88j78\68S7:D7<98>08@%9B:DKAL DM'HP4OUAUYIY\P]`U_aXadddfegjdkn_orTswEw}9{,ˆ'€ˆ!‚‹ƒŒƒ~‘ }” {–y™w›uœtrqžoŸm k¡g¤d¢f¥p¦ w¬ €± ˆ¶ º –½›Á Ä£Å¦Æ©Ç¬É¬Ì®Ð°Ó²Õµ×¸Ù»Ü¿áÃãÅåÇæÉçËçÌçÎçÏçÐèÑèÒèÓéÔéÔè ÓæÒäÒâ$Ðß.ÎÜ9ÌÙ@Ì×EËÖHÊÕKÉÔLÉÓMÈÒNÇÒRÅÏ\ÀÉe´½n«´r¥­€¢¨‡Ÿ¤ž¡“œž—™›š˜š—˜¤™š¨˜™¯››µ¸  »¤¤¹©©¶¯±´´¶´·¹´º½µ¿ÂµÀĶÂÅ·ÃÆ»ÄÇÀÆÈÅÇÈÅÊÌÆÎÐÈÒÔÊÕÖÏÙÚÕÚÛÜÜÜáÝÝâßßãááäããæååéèèìëëèíîçððìòòôõõö÷÷øøøùùùúúúûüüüüüüüüýüüýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþÿÿÿþþþUVtqˆŠtyPb®®‰ŠŠŽ‚T³b±²·ÀÊÛÚÀÝÿÿÿÿÇf‹Št ­‡‡Êèèèàè÷÷÷ÿÿÿÿÙ~ŠsV„‘‘¢èèÙÁèèèèè÷ÿÿÿÃqS…”“’‘‘€èÍÊàèèèèèèèÿÿÀ—••““’­Ï_÷èèèèèèèè÷ÿ_™˜–•”Œ¾ØÎèèèèèèèèèèÿNw››š™—†iÕèèèèèèèèèèèè赕›››†ƒÄèèèèèèèèèèèèè踄†††Á÷÷÷÷÷è·KKD$%LµÖ·nh¬Ò÷÷÷÷÷÷º0/.+)'%$AM L÷÷÷÷÷÷÷÷K10/-*(&$#!Ñ÷÷÷÷÷÷÷K310.-)(&$# '·÷÷è÷÷÷÷¹32./.,)'%$!,/Gá÷÷÷÷÷÷Ü`F¹ÇD-+('$.2)»÷÷÷÷÷÷÷÷÷÷÷Ð=-*(##2/@Ô÷÷÷÷÷÷÷÷÷÷÷`.,'",-JÙ÷÷÷÷÷÷÷÷÷÷·/-$%IÏ÷Ì«ÄÛ÷÷÷ÛI)((/:®zzzeÙÙ»G'%10OTU e-)((EmƒŸž™†vY*)?c¡×¡›£®P,,3_÷Ë¥Ôÿa--33ÇÎ[¥«]ÿÒ..3ÏÎËÛÚÆÿÙ//3¼ÿÿÿÿÿÿ¾0/3KÕÿȹèÙK13CK83?=2233333ÿÿü?ÿÿàÿüþüøððàààððüüøøø?ø?üÿÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿ€ÿÿÀÿÿÀÿÿÀÿÿàÿÿø?ÿ(0`€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ313333333330HwwHÿ÷ƒ330xˆx‡ÿÿ÷ÿÿÿÿÿƒ33x;»ÿÿÿÿÿÿÿÿÿø33;»»·ÿÿ÷ÿÿÿÿÿÿÿ303»»»³ÿÿwÿÿÿÿÿÿÿs;»»»³ÿwÿÿÿÿÿÿÿÿp»»»»¸÷ÿÿÿÿÿÿÿÿÿ€;»»»»¸wÿÿÿÿÿÿÿ÷;»»»»7wÿÿÿÿÿÿÿ÷»»»»³ÿÿÿÿÿÿÿÿÿÿ»»»»3ÿÿÿÿÿÿÿÿÿÿ€»»»³¸ÿÿÿÿÿÿÿÿÿÿÿ€;»»;?ÿÿÿÿÿÿwˆ‡ÿÿ€;33¸ÿÿÿÿÿ„DDDDH€33ÿÿÿÿøDDDDDDH@ÿÿÿÿÿôDDDDDDDÿÿÿÿÿôDDDDDDDÿÿÿÿÿôDDDDDDD@OÿÿÿÿÿôDDDDDDD@ÿÿÿÿÿøDDDDDDDDDHÿÿÿÿÿÿ„DxDDDD@DDÿÿÿÿÿÿ÷ÿtDDD@DDÿÿÿÿÿÿÿÿ÷DDDDDÿÿÿÿÿÿÿÿÿDDDDDÿÿÿÿÿÿÿÿDD@DOÿÿÿÿÿÿÿÿDD@Dÿ÷ÿÿÿÿDDDH÷33ÿÿtDDDƒ™™7ÿxD@DD0„DD@C;34DD3»»»³3DDƒ»û»;3DDHwû»wtDDG÷w»ÿ÷DDDOÿw·ÿ„DDðwwÿ„Dÿÿÿÿÿ„DOÿÿÿÿÿD@Gÿÿÿÿ÷D@DÿGÿôDH„DDD@DDDDDDDÿÿÿÿÿÿÿÿÿþÿÿÿÿøÿÿÿà?ÿÿà?ÿà?ÿÀ?ÿ€?ÿ?þÿüüø?ø?ø?øÿøÿüÿüÿÿ€ÿÿ€ÿÿ€ÿÿþÿþÿþÿþÿÿÿÿÿÿ€ÿÿðÿÿøÿÿüÿÿüÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿüÿÿÿüÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÀÿÿÿÿø?ÿÿ(0`€ &5$8"61'! "(18<BGKNQTVXZ]_`adghikmnopsuwx{}|zx v u uuttspn j!!j##m''s+,u00{55}78::ƒ??ˆDDIIMMŒQQŠSS†TT‚UV~TUyQSoLOZLPDNS.QXT^WaWcXe\j_nbrfyik…gŽf“b™[žV£T¦M®X¬ _® f® o­ {«„© ލ ’¨ ”ª —®˜²˜¶˜¹›»›½›ÅŸÊ¡Ï¨Ò±ÕºÙ¿ÞÂâÅæÈçÊèËèÍéÏéÒêÕëØëÚë×éÕç ÒâÌÛÉÕ ÄÐ#ÂÎ&ÀË,»Å1µÀ9«µC¡ªM–ŸT‘™ZŒ“d‡Œjƒˆp…vƒ‹—‚™„…ˆˆŸ¡©”•ª˜˜«›œ¦ž ¬¢£¯¥¦²§¨¯ª««­®©¯±¦±´Ÿ´·˜·»¹¾Š»¿…¾Ã…ÀÅ†ÂÆŠÆÊÈÌ•ÊÍžĘ́ÇÉ´ÃŽÁÁÁÁÁÄÁÁÈÁÁÈÄÄÌÇÇÏËËÒÍÍÒÐÐÔÑÑÖÓÔÜØÙÞÜÜàÞÞáááääääåææèèêëëîíîðððòòòóóóñõõöööø÷÷úùùûûûüüüüüüüüüüüüüüüüüüüüüüüüýýýýýýýýýýýýýýýýýýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþÿÿÿþþþgllbk€‚l‚‚uuk ijkv€‚|}‚‚hHZ®Åů^­É×ëäÖŤiv‚‚vk³¢¡¬¶«©Ç×ëëëëÊÜÿÿÿÿÿÿÿÿØ¢|u}‚hµ¯{†ˆ„žÝëëëëëëëöÿÿÿÿÿÿÿÿÿÿö¯€‚‚jy‡ˆˆˆˆ†ÂëëëëëÌëëëëëëöÿÿÿÿÿÿäž‚}bƒŠ‰‰ˆˆˆˆœëëëëÄËëëëëëëëëöÿÿÿÿÿ¹lƒ‹ŠŠ‰‰ˆˆˆwëë̶ÕÞëëëëëëëëëëÿÿÿÿ¯yŒŒ‹ŠŠ‰‰ˆžëÈÖëëäëëëëëëëëëëöÿÿÿZgŒŒ‹ŠŠˆ¯ÐÅÆöëëëëëëëëëëëëëöÿÌ ŽŽŽŒŒ†|ÌÐÑÍëëëëëëëëëëëëëëëÿÈbŽŽŽŠ{ ÔëëëëëëëëëëëäëëëëëëÿÞKgŽŽŽy~ÄØëëëëëëëëëëëëëëëëëëëëZ“Žz„¡ëëëëëëëëëëëëëëëëëëëëëë¥~†y†¡Úëëëëëëëëë×ÙÚβ§§¬ÆØëëë¦g’~{†ŸØööööööööä§ADD(&%$" EYÐöZ cy{zÃöööööööööö¬31.,*)&%#!  ªËöööööööööööäL430.+*(&$"! °öööööööööööÔ?642/-+)'%$"!XöööööööööööÒ27532.,*)&%#"!JßööööööööööÝF87431.,*(&%#! +Éöööëööööööö§876/'0-+*'%$"! +/%YööëööööööööÚS74OѨ?-+)&%$" 43/ÐöööööööööööÞÉÇÝööÇA,*)&%"+73'Zöööööööööööööööööö­.+*(&"97.²öööööööööööööööööÝG-+*&!722 ÏöööööööööööööööööQ.-*%"%+JÒööööööööööööööööP1-)'HÊöööØÁÁÖöööööööÑA*'&!/0§Ù›spvÁäööööÏP()10.^vppppc¾ëßÇW&$%422Mi b£E&&%%%3Mybk”wbyJ('&&2ze‰‘™~ey%)')4¡”‘Û™š–i&*)*73WÓ¾ä–ÁÑÈM+**+77ÇÿÎÀ¾–ÖÿÿÊ.++-88HßöÓ»¾»ÏÿÿöT-,-99Tÿ×Í¿¾ÎØÿÿ§...9SÿöÓëÿÿö×ÿÿÿW0/19GÞÿÿÿÿÿÿÿÿÿäM219³ÿÿÿÿÜÿÿÿÿÉ4329IÍÿÿÒN­ßÿÒM449@TU@98FMB77999999988799998ÿÿÿÿÿÿÿÿÿþÿÿÿÿøÿÿÿà?ÿÿà?ÿà?ÿÀ?ÿ€?ÿ?þÿüüø?ø?ø?øÿøÿüÿüÿÿ€ÿÿ€ÿÿ€ÿÿÿþÿþÿþÿþÿÿÿÿÿÿ€ÿÿðÿÿøÿÿüÿÿüÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿüÿÿÿüÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÀÿÿÿÿø?ÿÿ(@€ €€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ33313333303333333‡wxƒ33330H‡ÿÿˆÿÿÿÿÿx3330xˆwx‡ÿÿÿÿÿÿÿÿÿÿÿs333w3»³ÿÿÿÿÿÿÿÿÿÿÿÿ÷333‹»»»?ÿÿÿÿÿÿÿÿÿÿÿÿÿs30»»»»·ÿÿÿøÿÿÿÿÿÿÿÿÿó3;»»»»³ÿÿ÷ÿÿÿÿÿÿÿÿÿø0»»»»»³ÿ÷‡ÿÿÿÿÿÿÿÿÿÿø;»»»»»³ÿwÿÿÿÿÿÿÿÿÿÿÿø»»»»»»¸÷ÿÿÿÿÿÿÿÿÿÿÿÿð»»»»»»7w‡ÿÿÿÿÿÿÿÿÿÿÿ€;»»»»»»?‡ÿÿÿÿÿÿÿÿÿÿÿ€»»»»»»;ÿÿÿÿÿÿÿÿÿÿÿÿÿp»»»»»»3ÿÿÿÿÿÿÿÿÿÿÿÿÿð»»»»»³¸ÿÿÿÿÿÿÿÿÿÿÿÿÿÿô»»»»»;?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿô;»»»³³ÿÿÿÿÿÿÿÿÿÿÿÿÿÿô;»»³;7ÿÿÿÿÿÿ÷‡x„DDˆÿô ³3;³ÿÿÿÿÿÿ„DDDDDDDô3»3ÿÿÿÿÿÿøDDDDDDDDHpÿÿÿÿÿÿÿôDDDDDDDDDÿÿÿÿÿÿÿÿtDDDDDDDDD@ÿÿÿÿÿÿÿÿ„DDDDDDDDDDÿÿÿÿÿÿÿÿ„DDDDDDDDDDÿÿÿÿÿÿÿtDDDDDDDDDDDÿÿÿÿÿÿÿôDDDDDDDDDDDÿÿÿÿÿÿÿøDDDDDDDDDD@DÿÿÿÿÿÿÿÿDDHtDDDDDDDDDÿÿÿÿÿÿÿÿøˆÿ„DDDDDDD@ÿÿÿÿÿÿÿÿÿÿÿøDDDD@DDOÿÿÿÿÿÿÿÿÿÿÿÿDDDD@DDÿÿÿÿÿÿÿÿÿÿÿÿtDDDDD@ÿÿÿÿÿÿÿÿÿÿÿôDD@DDÿÿÿÿÿÿÿÿÿÿÿôDD@DDÿÿÿÿÿÿÿÿÿÿôDDDGÿÿÿwÿÿÿÿÿÿtDDDÿx³8ÿÿÿÿÿD@DDw³7ÿÿÿtD@DD™™÷„DDDD08DDD@DC33DDD@D³»»»34DDC3»·‹»»3°DDHƒ»¿‹»;³°DDDˆ‡¿‹»‡xDDDHÿ¿{»ÿ„DDDOÿ{³ÿÿôDDDÿû{·ÿÿ÷DDDø‡{‡ÿÿDD@Døÿÿ‡ÿÿDDDÿÿÿÿÿÿ÷DDDÿÿÿÿÿÿ÷DDOÿÿÿÿÿÿøD@GÿÿÿÿÿtDDÿøGÿ÷DDDˆDDDDD@DDDDDD@DDDDDÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿ€ÿÿÿÿÿþÿÿÿÿÿüÿÿÿÿðÿÿÿÁÿÿþÿÿüÿÿøÿÿðÿÿàÿÿÀÿÿ€ÿÿÿÿÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿàÿÿðÿÿðÿÿøÿÿàÿÿàÿÿÀÿÿÀÿÿÀÿÿÀÿÿÀÿÿàÿÿà?ÿÿð?ÿÿøÿÿÿ€ÿÿÿÿÀÿÿÿÿàÿÿÿÿàÿÿÿÿàÿÿÿÿðÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿà?ÿÿÿÿàÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿøÿÿÿÿøÿÿÿÿÿüÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿ(@€n77q88p44k-.i&&j"#mqv€|ytpmkmookgec`_^__``^\[YXWUQMLKKJHEB > < 9 6 3.,-,+& " '&/+6.94A :G@MDQGTKWQ^Wg[o^u`|_ƒ]‰X’ PœI¢A©7²;´R§d›p–t”x’€’‡™Ž¡‘¥“®”¶—À˜ÌšÑ›ÑŸÐ¥Î­Ë³Ì·Í»Î¾ÒÂׯÜÌáÖêÚìÙëÖêÐéÍèÊæ#Å×=¿ÌKÀÊP³¼V§°\¥bŽ•d…‹l|pvyvqryll}hh…jj•nn™ww™}}š‚‚™„„›ˆˆ‹‹¢¨“”¦˜™¨ ¡¬¤¥¯©©®­­²±±²³´³µ¶³·¸·º»¿½½¿¿¿ÀÂÃÂÅÆÅÇÈÃÊÊÅÍÎÅÏÐÌÒÒÎÕÕÔØØØÚÛÛÞÞßàááââäääçææëêêíììîííðîîñððòññóóóðôõðõöòö÷øøøùùùúùùúúúûûûûûûüüüüüüûüüüüüüüüüüüýüüýüüýüüýýýýýýýýýýýýýýýýýýýýýþýýþýýþþþþþþýþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþþÿÿÿþþþHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHAAKQWYSTHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHAPZblawxxvXXAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHATbvxxlwxxubvuTAHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHAXmmruxtxxwbxxxtQHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH9;<<<<<>O–›£ª«¦ ”^lwmvxxxwZQHHHHHHHHHHHHHHHHHHHHHHHHHHQLOAH99—Ÿ«½ÐîÆš½ÿÿÿÿÿÿÿÿî­–lxxwkksQHHHHHHHHHHHHHHHHHHHHHHHA¤‘¥¶©œ¯ÙÙÙÙÙÙÙº¾ÿÿÿÿÿÿÿÿÿÿÿÿÿ¦_xkvxxZHHHHHHHHHHHHHHHHHHHHHH­£z‰‰n°îîÙÙÙÙÙÙÙÙÙîÿÿÿÿÿÿÿÿÿÿÿÿÿÿ´axxxx_HHHHHHHHHHHHHHHHHHHHHK}‰‰‰‰‰‰oÇÙÙÙÙÙÙÙÉÙÙÙîîîÙÙÿÿÿÿÿÿÿÿîÿ¥txxuRHHHHHHHHHHHHHHHHHHHHUsˆˆ‰‰‰‰‰‰®ÙÙÙÙÙÙ¾ ÙÙÙÙÙÙÙÙÙîîÿÿÿÿÿÿÿÿnxuVAHHHHHHHHHHHHHHHHHHHZ{ˆˆˆˆ‰‰‰‰‰‰ÙÙÙÙÙ¶ÆÙÙÙÙÙÙÙÙÙÙÙÙîÿÿÿÿÿÿlSAHHHHHHHHHHHHHHHHHHHX|ˆˆˆˆˆˆˆ‰‰‰‰nÐÙÙµ¢§ÙÉÙÙÙÙÙÙÙÙÙÙÙÙÙîÿÿÿÿÿœOHHHHHHHHHHHHHHHHHHHHOt‡ˆˆˆˆˆˆˆˆ‰‰‰‘ÙÙª°îÙÙÉÙÙÙÙÙÙÙÙÙÙÙÙÙÙÿÿÿÿÿ–==HHHHHHHHHHHHHHHHHHUn‡‡‡ˆˆˆˆˆˆˆˆˆ‰‘Ù­¸ÿÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙîÿÿÿÄ6==HHHHHHHHHHHHHHHHHHX‚‡‡‡‡‡ˆˆˆˆˆˆˆtª¸¶“±ÿÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙîÿÿ–====HHHHHHHHHHHHHHHHLq†‡‡‡‡‡‡‡ˆˆˆˆaƱ١·ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÿÿ====HHHHHHHHHHHHHHHHT‡†††‡‡‡‡‡‡ˆˆq’ºÂÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙîÿî¤====HHHHHHHHHHHHHHHH`ƒ††††‡‡‡‡‡‡}sn·ºÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÐÙÙÙÙÙÙÙÙîî¿5===HHHHHHHHHHHHHHHH`„ƒƒ††††‡‡‡‚o€’Ù¿ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙ<==HHHHHHHHHHHHHHHHT…„ƒƒƒ†††‡‚n‚_ÀÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙ;HHHHHHHHHHHHHHHHHHA{„„„ƒƒƒ†o‚n¯ÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÙÒÒÙÙÙÙÙÙÙÙ9HHHHHHHHHHHHHHHHHHHo„„„„„‚qr†p¨îîîîîîîîîîîîî­Ÿ«ª–.,/Ÿ´îîîî9HHHHHHHHHHHHHHHHHHHS‚srqr‡‚n®îîîîîîîîîîîîÉ—!"$&(()).¬î¾9HHHHHHHHHHHHHHHHHHHATns€‚{o¡Âîîîîîîîîîîîîî!"%&())++.—§,9HHHHHHHHHHHHHHHHHHHHHHXS–¯ÄîîîîîîîîîîîîîîÁ$%'())+,+9,9HHHHHHHHHHHHHHHHHHHHHHHA—îîîîîîîîîîîîîîîî¨!"$%'()*,,,;9HHHHHHHHHHHHHHHHHHHHHHHAîîîîîîîîîîîîîîîîž!"$&(()*,,,?HHHHHHHHHHHHHHHHHHHHHHHH9»îîîîîîîîîîîîîîî›"%'())+,,;AHHHHHHHHHHHHHHHHHHHHHA@=¢îîîîîîîîîîîîîîî£$%'()*,,9AHHHHHHHHHHHHHHHHHHHHH>),–îîîîîîîîîîîîîîî¹ !"$&'()*,,AHHHHHHHHHHHHHHHHHHHHA*4ÀîîîîÐîîîîîîîîîî˜ .!"%&())*,HHHHHHHHHHHHHHHHHHHHH<9 îîîÙîîîîîîîîîîî½  ´$%'()*;HHHHHHHHHHHHHHHHHHHHH)"Æîîîîîîîîîîîîîîî½›—¯îîЛ!"$%'),HHHHHHHHHHHHHHHHHHHHHH*9¡îîîîîîîîîîîîîîîîîîîîîîîî›!"$%)9HHHHHHHHHHHHHHHHHHHHHH?1½îîîîîîîîîîîîîîîîîîîîîîîî!"",AHHHHHHHHHHHHHHHHHHHHHHH( ;—îîîîîîîîîîîîîîîîîîîîîîîî§!)9HHHHHHHHHHHHHHHHHHHHHHHH@ "9¢îîîîîîîîîîîîîîîîîîîîîîî¾!,HHHHHHHHHHHHHHHHHHHHHHHHHH@*3­îîîîîîîîîîîîîîîîîîîîîîÉ'AHHHHHHHHHHHHHHHHHHHHHHHHHHHA9,,"1¯îîîîîîîîîîîîîîîîîîÙîî½&$9HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?(%#«îîîîîîµ°¼îîîîîîîîÙîî¤%HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH?˜¿îî¶~rpŽ»îîîîîîîî»)9>>Ikz«`˜®ÿR“¬ÿ °Óÿ©Õÿ²Øÿ¹âþ–Îö2E^“””N`ˆ“”¡¤›œžŸo§““ŠÆººÔÙÒÒüùøøÿöööÿ¿½½ÿúúúÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÂÈÊÿ2„£ÿµÜÿ²Øÿp™¾W§µ»°ÌþÂæÿ¬Ìÿ³ÐÖÿýýýÿýýýÿýýýÿüüüÿýýýÿþþþÿþþþÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿôôôÿ1‹­ÿ¯ÞÿsžÌEO¦¿ÊÇèÿÆçÿÄçÿÃçÿ;·ÎÿýýýÿýýýÿôôôÿÀÀÀÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿ¨ÂÍÿv¢ç)95  ªÂÐÌéÿÊèÿÈèÿÇèÿÆçÿ ¹ÿýýýÿÖÖÖÿÓÓÓÿüüüÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿÿÿÿÿÿÿÿÿ¿»»ý:¡•ÐêÿÎéÿÍéÿËéÿÊèÿÈèÿL¨·ÿÞÞÞÿdddÿþþþÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿjVVÿÑ&* Â×ûÒêÿÑêÿÏêÿÎéÿÌéÿ¸Óÿ˜±µÿïïïÿÜÜÜÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿÿÿÿÿT>>ÿÿMhrRÖìÿÕëÿÓëÿÒêÿÐêÿ³Êÿ’¢ÿëëëÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿš‹‹ÿ Ø^"Îàþ×ìÿÖëÿÔëÿ¶Êÿ£¶ÿµÆÈÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿ­žžÿ+c¨µÏÅÖÿ¸Èÿ°Áÿ ¬¼ÿž¿Ãÿþþþÿþþþÿþþþÿþþþÿþþþÿýýýÿ®ŠŠÿ‘QQÿŠPPÿj##ÿOÿP ÿl66ÿ ……ÿðîîÿ«““ÿ1c(+Œ—x%”žÛu¼ÃÿÝâãÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ¼¡¡ÿrÿmÿhÿbÿ]ÿWÿRÿMÿYÿ^--ÿ6Fc99‹þþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ”PPÿwÿqÿlÿfÿaÿ[ÿVÿPÿKÿ>ÿ$ ìááÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ‘IIÿ{ÿuÿpÿjÿeÿ_ÿZÿTÿOÿIÿ/e7aWÿ«““ÿþþþÿþþþÿýýýÿþþþÿþþþÿþþþÿþþþÿ¹’’ÿÿyÿiÿmÿiÿcÿ^ÿXÿSÿMÿ@Zd¶mÿe,,ÿýüüÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿùùùÿ ggÿ((ÿ¶——ÿÑÃÃÿw!!ÿgÿbÿ\ÿWÿNí) iªxÿ^ÿ¼¥¥ÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿåßßÿnÿeÿ`ÿ\ÿGƒI4zöoÿUÿïææÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿžjjÿjÿdÿXÙ0 A>cÉeõp66ÿöððÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ§{{ÿnÿe÷P:SÔƒ11ÿçÖÖÿþþþÿÇãäÿf¼Æÿ•ÈËÿøøøÿþþþÿþþþÿþþþÿúøøÿ€11ÿ^Í\E[=nÿqÿn™žÿV¬ÿÓÿËÿ=œÿîõöÿ÷òòÿÄ££ÿs**ÿW²SurúJ36ÿ<@ÿ ÿ?uÿ)ÿÿ7yÿdÿ]ÿ[þ[0U%'å‹”ÿ¥°ÿ ÞêÿÐÜÿÓáÿ¬¶ÿlsÿ3QVÿaÿ_ãiàY‹ÿ0Í×ÿ•ðöÿ'ÎÙÿàîÿ?ÂÊÿA§­ÿ::=ÿdÿcâ{íª]]ÿþþþÿ“×ÜÿJÖßÿ Öãÿãêêÿÿÿÿÿ¨llÿgÿfä€õáÄÄÿÝÝÝÿGZ[ÿHÐÙÿkÄÊÿZZZÿÿÿÿÿîââÿkÿiÚ‚½ìÙÙÿÝÝÝÿÔÔÔÿ÷øøÿ÷÷÷ÿÂÂÂÿÿÿÿÿøòòÿnÿl „NÕ©©ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ×¶¶ÿqþoN†¥AA õêêÿÿÿÿÿåÊÊÿ˘˜ÿþýýÿøññÿ”88ÿu‡• 88ö…ÿÿ‰ÿ…ïzjx‡†V„dQ€ÿÿü?ÿÿàÿüþüøððàààððüüøøø?ø?üÿÿÿ€ÿÿ€ÿÿÀÿÿÀÿÿÀÿÿ€ÿÿÀÿÿÀÿÿÀÿÿàÿÿø?ÿ(0` €%N;R“6JÁ\~Øq›ßpšÚD]3G6Jdj’á‘Æÿ›ÕÿsžÿœÖÿœÖÿ{©ÿx¥ÿl”ê 3% -:©f†ÿhŠÿn”ÿ‚±ÿÅÿœÖÿ…¶ÿ‡¹ÿœÖÿœÖÿ`ƒÝ 1CN{aa~ª››Â¸¸­Â¸¸¯©žž¯D::¯˜˜˜ÖÉÉÉþîîîÿýýýÿüüüÿìììÿÁÁÁÿv‹“ÿkŠÿ‚²ÿœÖþœÖÿƒ´ÿm–ÿ)9j©©©]hvx·d‰è…”—í°¸ºÔ“‘’—žˆˆºÇ»»øñîîÿýýýÿýýýÿýýýÿýýýÿÌÌÌÿöööÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðððÿl„ÿ†·ÿz§ÿˆºÿœÖÿ_‚­±±±{¡§Ê$³þ¼ßÿÁæÿ°ÓÿL‘Ÿÿõøøÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿ†™ ÿÅÿœÖÿœÖÿeŠÅ19$”©Ì ÃãÿÅçÿÄçÿÃçÿÂæÿ»ßÿ¨ÊÑÿýýýÿýýýÿýýýÿýýýÿýýýÿÐÐÐÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿüüüÿ;‚ÿœÖÿ‡ºþ#1aEO6¨ÂäÉèÿÈèÿÇèÿÆçÿÅçÿÄçÿÂæÿ7£¸ÿýýýÿýýýÿýýýÿýýýÿºººÿÎÎÎÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿŽ®»ÿpšö%3o29+­ÅîËéÿÊèÿÉèÿÈèÿÇèÿÆçÿÅçÿÄçÿŒ¢ÿýýýÿýýýÿÐÐÐÿµµµÿçççÿúúúÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ£ŸŸú 6 •¨ÐÎéÿÍéÿÌéÿËéÿÊèÿÉèÿÈèÿÇèÿÆçÿC‘ŸÿýýýÿÆÆÆÿéééÿýýýÿýýýÿüüüÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿziiÿÁkw‰ÏèÿÐêÿÏêÿÎéÿÍéÿÌéÿËèÿÊèÿÉèÿÆæÿœ¡ÿÛÛÛÿÁÁÁÿÃÃÃÿþþþÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÕÏÏÿ*ÿÿd«¾õÓëÿÒêÿÑêÿÐêÿÏéÿÎéÿÌéÿÌéÿ¿Üÿž¶ÿÏÐÐÿÝÝÝÿÞÞÞÿÒÒÒÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿÿÿÿÿËÄÄÿÿÿÙPXxÕëÿÔëÿÓëÿÒëÿÑêÿÐêÿÏêÿÎêÿÊæÿ™¯ÿA…ÿæææÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿüüüÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿÿÿÿÿúúúÿ@%%ÿÿÿ/kus×ìÿÖìÿÕëÿÔëÿÓëÿÒêÿÑêÿÐêÿ”¦ÿ£¸ÿ»ÀÁÿðððÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿv__ÿ#þ"%<ÔçþØìÿ×ìÿÖëÿÕëÿÔëÿÒêÿ˜ªÿ·ÎÿeŒ‘ÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿ…nnÿ)”©·ïÚìÿÙìÿØìÿÖìÿÅÙÿ”¤ÿ¿Ôÿ=„ÿóóóÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿíììÿóññÿôôôÿ×ÔÔÿ¶§§ÿœÿ™ÿ¨““ÿĽ½ÿðððÿýýýÿýýýÿýýýÿ‡ooÿ.—qz¡Öçÿ°¾ÿ©¸ÿ¢±ÿ¬¼ÿÁÔÿQ–ÿíïðÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿüüüÿ˜ssÿj ÿjÿ`ÿ`ÿ^ÿZÿWÿSÿOÿTÿ``ÿÞÛÛÿþþþÿ~UUÿ2”[cy–¡Å¤±ö¡®ÿ@›£ÿ´ÀÁÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ¬‘‘ÿrÿoÿlÿhÿdÿaÿ]ÿYÿVÿRÿNÿLÿO ÿ ‹‹ÿH ÿ5t(gßÎÏÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿüüüÿu%%ÿvÿrÿnÿkÿgÿcÿ`ÿ\ÿXÿTÿQÿMÿJÿFÿ3ÿ8BIÀ¢¢þþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿåååÿrÿxÿuÿqÿmÿjÿfÿbÿ_ÿ[ÿWÿTÿPÿLÿIÿ?ÿ/‰ffüþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿâââÿqÿ{ÿwÿtÿpÿlÿhÿeÿaÿ]ÿZÿVÿSÿOÿKÿHÿ+‚!6ÊQ!!þûúúÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿøøøÿuÿ~ÿzÿvÿsÿoÿkÿhÿdÿ`ÿ]ÿYÿUÿQÿNÿJÿ8¸?¹fÿ@ÿÛÇÇÿþþþÿþþþÿþþþÿýýýÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ¥zzÿ€ÿ}ÿyÿmÿ_ÿnÿjÿfÿcÿ_ÿ[ÿXÿTÿPÿMÿCrf÷mÿZÿ]]ÿþþþÿþþþÿýýýÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿôóóÿ…BBÿ|ÿuÿx66ÿßÝÝÿ¢ƒƒÿjÿjÿfÿbÿ^ÿ[ÿWÿSÿMõ)4"uÿtÿmÿAÿéÜÜÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿúøøÿÎÅÅÿȾ¾ÿøøøÿþþþÿþþþÿȺºÿk ÿhÿdÿaÿ]ÿZÿTÿE£gé{ÿtÿ_ÿ|UUÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ²˜˜ÿkÿgÿcÿ`ÿ^ÿSù3+Lpþ{ÿlÿ7ÿÅ©©ÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿø÷÷ÿnÿjÿfÿcÿ^ÿD„P{ÿpÿqÿMÿæÕÕÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ€::ÿlÿiÿeÿZÑ 5XTÆZèfÿ\!!ÿìààÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿx77ÿoÿjÿaíE)0_ÿpÿßÉÉÿþþþÿþþþÿþþþÿîññÿœÐÓÿšÃÆÿêëìÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿåÝÝÿj ÿeÿ_æ]3R»mÿnÿ±xxÿ÷òòÿ«ÓÖÿ°»ÿh°ÿE¯ÿ–ÿÉÌÿüüüÿþþþÿþþþÿþþþÿþþþÿãÕÕÿ~88ÿ`ëIFbBoÿnÿlÿI`dÿ ƒÿÂÿîÿâÿàÿSgÿxÈÍÿýýýÿüûûÿж¶ÿšccÿ]ÿWÿYeuqýpÿM,/ÿ|„ÿÿ#ÿ cÿFÿ/ÿÿW\ÿmvyÿkÿ^ÿ\ÿ[ÿ[×Y tåN,.ÿ ”ÿÿU[ÿ‰‘ÿÑÞÿ‹“ÿU[ÿ69ÿ ÿ •žÿT!ÿ`ÿ_ÿ]ÿ^spÔ ¢¬ÿhnÿÉÖÿàîÿBèòÿ(ÂËÿàîÿàîÿàîÿ¯¹ÿciÿ ˜¢ÿZÿaÿ_ÿaSuÐW„‰ÿ;¢©ÿÐÝÿ ÝëÿÂ÷úÿ4ÂËÿàîÿàîÿ·Âÿ ÈÔÿ&¢ªÿ€ˆÿ^ÿcÿbÿcTzÖsÿ‹VVÿ¹ãåÿuÉÏÿíüýÿNÍÕÿàîÿàïÿ›ËÎÿÓÝÞÿÃÆÆÿw((ÿgÿeÿdÿgV|ê{ÿ׳³ÿÿÿÿÿ¹ÓÔÿ™ëðÿuÒØÿàîÿÉÕÿéêêÿÿÿÿÿÿÿÿÿßÊÊÿkÿgÿfÿiW€ùŒÿýûûÿþþþÿãããÿl½ÂÿˆÎÓÿßíÿz¾ÂÿÙÙÙÿÿÿÿÿÿÿÿÿþþþÿ“FFÿjÿhÿiSÒ¢FFÿÿÿÿÿîîîÿÿÑÒÒÿŽÙÞÿ‡ÖÛÿÓÓÓÿÿïïïÿÿÿÿÿÿÿÿÿ²zzÿlÿkül'ƒ†£DDÿÿÿÿÿþþþÿäääÿýýýÿÿÿÿÿÿÿÿÿþþþÿîîîÿÿÿÿÿÿÿÿÿÿÿÿÿ§ccÿnÿmÚo„#‘öüùùÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿýüüÿ‰))ÿpÿo˜…‚׬¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúõõÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàÅÅÿuÿsàq‡• ¹éÑÑÿÿÿÿÿÿÿÿÿðààÿ–//ÿË™™ÿýûûÿÿÿÿÿïááÿŽ))ÿvèu2‡ Œ ’©IIü«OOÿˆ ÿÿ€ÿŠÿ“,,ÿ‚ ÿzÇz#ˆ‡=†®…í„þ‚ÿöÈ~c|† †…ƒ€ÿÿÿÿÿÿÿÿÿþÿÿÿÿøÿÿÿà?ÿÿà?ÿà?ÿÀ?ÿ€?ÿ?þÿüüø?ø?ø?øÿøÿüÿüÿÿ€ÿÿ€ÿÿ€ÿÿþÿþÿþßþÿÿÿÿÿÿ€ÿÿðÿÿøÿÿüÿÿüÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿþÿÿÿüÿÿÿüÿÿÿþÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÿÿÀÿÿÿÿø?ÿÿ(@€ BDj w3F@X€ ,g&4pB[Ë]€üq›ÿ[|ÿšÓÿœÖÿœÖÿ•Íÿ9Oû7Ly&4mb†é•ÌÿœÖÿœÖÿhÿšÓÿœÖÿœÖÿÁÿ]€ÿ“ÊÿŒÀÿ$1« 9O¯tžÿv¡ÿ|ªÿÁÿœÖÿ…¶ÿœÖÿœÖÿšÓÿ`„ÿœÖÿœÖÿœÖÿˆ»ÿ r6*&2&>$@#@"@@ iZZZº‚‚ô¤¤¤ÿºººÿ¾¾¾ÿ±±±ÿ“““ÿgquÿ Sgÿl“ÿšÓÿtŸÿ“ÊÿœÖÿœÖÿœÖÿšÓÿBZñ777;?2 80AX44‹†nnÇ¢õÆ»»ÿîììÿüüüÿþþþÿøøøÿƒÿ‹‰‰ÿìììÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþþÿÁÁÁÿK`iÿj‘ÿœÖþœÖþ˜Ðÿe‹ÿeŠÿ±ÿ`ªªªhˆ‰‰Ò^…üv¢«ÿ—¬°þÏÚÜþ®¶¸õŽˆˆ¿›„„æÒÈÈÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿäääÿíííÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ°°°ÿZtÿœÖÿd‰ÿ—ÏÿœÖÿœÖÿC[šÃÃÃ+š¤¦×=Ÿü¦ÅÿÁæÿÁæÿ¸Üÿv‰ÿÃËÍÿþþþÿþþþÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿÿÿþÿÿÿÔÔÔÿ_|ÿœÖÿœÖÿœÖÿœÖÿRp¹X”žì ·ÕÿÄæÿÄæÿÃçÿÂæÿÁæÿÁæÿ”ÿöùùÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿúúúÿýýýÿýýýÿýýýÿþþþÿþþþÿþþþÿýýýÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿÿÿÿÿÿ©¬¬ÿ„´ÿœÖÿœÖÿÂÿ#x,3D™±ïÇèÿÆèÿÆçÿÅçÿÄçÿÃçÿÂæÿÂæÿ¾âÿ«ÅËÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿíííÿ“““ÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ.rŒÿœÖÿÆÿ->ÃEO`¯ÉôÉèÿÈèÿÈèÿÇèÿÆçÿÆçÿÅçÿÄçÿÃçÿÂæÿ:•¦ÿýýýÿýýýÿýýýÿýýýÿýýýÿÚÚÚÿˆˆˆÿ÷÷÷ÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿr™¨ÿl”þ ,Ÿ @HU³ÌýËéÿÊèÿÊéÿÉèÿÈèÿÇèÿÇèÿÆçÿÅçÿÄçÿÄçÿyÿüüüÿýýýÿýýýÿ×××ÿžžžÿ³³³ÿýýýÿúúúÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ†„…÷ ; .¢¸ðÎéÿÍéÿÌéÿËéÿÊèÿÊèÿÉèÿÈèÿÇèÿÇçÿÆçÿÅçÿ>}ˆÿýýýÿýýýÿ»»»ÿËËËÿþþþÿýýýÿýýýÿúúúÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿwffÿ¬ .3 z‰ÒÐêÿÏêÿÎéÿÍéÿÌéÿÌéÿËèÿÊèÿÉèÿÈèÿÈèÿÇèÿÅæÿs„‡ÿýýýÿÃÃÃÿàààÿÿÿÿÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿÿÿÿÿöõõÿ& ÿýd@G{ÌäÿÑêÿÐêÿÏêÿÎêÿÎéÿÍéÿÌéÿÌéÿËèÿÊèÿÉèÿÈèÿ¶ÿ¸¸¸ÿßßßÿÙÙÙÿxxxÿÎÎÎÿÿÿÿÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿÿÿÿÿlYYÿÿÿä  îÓëÿÒêÿÑêÿÐêÿÐêÿÏêÿÎéÿÎéÿÍéÿÌéÿËéÿÆäÿÊèÿkyÿøøøÿÍÍÍÿýýýÿšššÿÜÜÜÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿÿÿÿÿÿÿÿÿbNNÿÿÿÿk*/tÑæÿÔëÿÓëÿÓêÿÒêÿÑêÿÐêÿÏêÿÏêÿÎéÿÍéÿÌéÿŽ¢ÿÂÞÿo|~ÿäääÿôôôÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿÿÿÿÿþþþÿ³ªªÿÿÿÿÊhr·ÖìÿÕëÿÕëÿÔëÿÓëÿÒêÿÒêÿÑêÿÐêÿÏêÿÏêÿµÍÿ˜®ÿt„ÿÞÞÞÿäääÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿüüüÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿþþþÿþþþÿðïïÿ, ÿÿÿþhrØìÿÖìÿÖëÿÕëÿÔëÿÔëÿÓëÿÒêÿÑêÿÐêÿÉâÿ€ÿ¾×ÿk|~ÿýýýÿîîîÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿP22ÿ&À¢z),ZØëÿØìÿ×ëÿ×ìÿÖëÿÕëÿÔëÿÓëÿÒëÿÌãÿx†ÿËäÿ foÿðððÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿ\>>ÿ)į¾ûÙìÿØìÿØìÿ×ìÿÖëÿÖëÿÕëÿ½Ñÿ|ŠÿÌäÿ |ŠÿÆÆÆÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýüüÿýüüÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿýýýÿ^@@ÿ-É‹ÈÛìÿÚíÿÙìÿØìÿØìÿÏãÿ›ÿ”£ÿÕëÿ†”ÿ³¶¶ÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿÈÃÃÿ«ÿĹ¹ÿ¼ººÿ‡‡ÿƒ]]ÿg11ÿFÿAÿBÿV!!ÿoJJÿžŽŽÿÖÕÕÿþþþÿþþþÿþþþÿþþþÿc??ÿ0É%(uÏàÿÀÐÿ ®ÿš¨ÿ”¡ÿ•£ÿÐãÿÍàÿ zƒÿÂÄÅÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿúúúÿ„bbÿmÿlÿiÿfÿhÿaÿ^ÿ[ÿXÿUÿSÿPÿQÿJÿc==ÿÅÀÀÿþþþÿòííÿX##ÿ2Ä ,/r{„Í¡®ýÆÖÿÐâÿ¯¾ÿ†ÿ…™›ÿóôôÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿž††ÿrÿpÿmÿkÿhÿeÿcÿ`ÿ]ÿZÿXÿUÿRÿOÿLÿLÿKÿjjÿÆ´´ÿ?ÿ5¤DH "w|[[÷ÄÆÇÿõõõÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿñññÿdÿuÿrÿoÿmÿjÿgÿdÿbÿ_ÿ\ÿZÿWÿTÿQÿOÿLÿKÿLÿ4ÿ>ÿ8r6Š\\ñþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ»¶¶ÿyÿwÿtÿrÿoÿlÿiÿfÿdÿaÿ^ÿ[ÿYÿVÿSÿQÿNÿKÿIÿFÿ)ÿ6A W''ûþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ¢‰‰ÿyÿyÿvÿtÿqÿnÿkÿiÿfÿcÿ`ÿ^ÿ[ÿXÿUÿSÿPÿMÿJÿHÿBÿA/Îïææÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿžƒƒÿ{ÿ{ÿxÿuÿsÿpÿmÿjÿhÿeÿbÿ_ÿ]ÿZÿWÿUÿRÿOÿLÿJÿHÿ)¤UßÄ¡¡ÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ¯££ÿ|ÿ}ÿzÿxÿuÿrÿoÿlÿjÿgÿdÿbÿ_ÿ\ÿYÿWÿTÿQÿNÿKÿIÿ6ÞuOþEÿ€SSÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿâááÿqÿÿ|ÿyÿvÿtÿqÿnÿlÿiÿfÿdÿaÿ^ÿ[ÿXÿVÿSÿPÿMÿKÿ@ßM÷hÿbÿ: ÿõððÿþþþÿþþþÿþþþÿþþþÿüüüÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ’ffÿÿ~ÿ{ÿyÿsÿLÿjÿnÿkÿhÿeÿcÿ`ÿ]ÿZÿXÿUÿRÿOÿMÿB‡%Vqÿmÿgÿ4ÿ·’’ÿþþþÿþþþÿþþþÿýýýÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿìëëÿt//ÿ€ÿ}ÿuÿjÿ¦””ÿÕÓÓÿu@@ÿlÿkÿgÿeÿbÿ_ÿ\ÿZÿWÿTÿRÿN÷)4Oƒxÿsÿmÿ]ÿKÿúøøÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿðììÿžÿ‚EEÿ‡]]ÿÊÆÆÿþþþÿþþþÿüüüÿœÿiÿjÿgÿdÿaÿ^ÿ\ÿYÿVÿQÿD¾Nv}ÿxÿrÿmÿ5ÿ¼——ÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿœ‚‚ÿnÿiÿfÿcÿ`ÿ^ÿ[ÿYÿPþ0J4sÿ}ÿxÿrÿcÿDÿóëëÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿx>>ÿlÿhÿeÿbÿ`ÿ^ÿ]ÿEÃU±ƒÿ}ÿxÿlÿ)ÿŒZZÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿ½´´ÿmÿjÿgÿdÿbÿ`ÿQó00c܃ÿ}ÿ]ÿnÿ2ÿÃÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿíííÿ`ÿlÿiÿfÿdÿbÿ>t$MÑwÿ}ÿwÿiÿ=ÿÛÂÂÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿúúúÿ_ÿnÿkÿhÿeÿWÉ3c@ÂK×fÿ^ÿKÿßÈÈÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿýýýÿþþþÿþþþÿìììÿXÿpÿmÿhÿ\ß-RSõYÿ^ ÿÕ¸¸ÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿÔÙÚÿ¿ÊËÿåééÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿýýýÿþþþÿþþþÿ·§§ÿkÿnÿjÿdØY"\fþlÿkÿªmmÿõîîÿþþþÿþþþÿÕÜÝÿM³¹ÿ¾Éÿ©ÿ™ÿh§«ÿææçÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿþþþÿíååÿuÿhÿQ¾6y"Z¯nÿmÿlÿ}ÿЯ¯ÿ¨ÉËÿ»Æÿwœÿ³ÿÚÿ °ÿX“ÿ)ª²ÿ½ÔÖÿþþþÿþþþÿþþþÿþþþÿþþþÿûúúÿ¹••ÿbÿVÿY§S^Fpÿnÿmÿlÿ9>Aÿ±¼ÿ#ZÿóÿûÿÅÿúÿñÿ8ÿ ¦°ÿ¿ÛÝÿþþþÿþþþÿàÏÏÿ¨vvÿnÿYÿXÿWù[(u qÿpÿoÿL#&ÿ±¼ÿÿÿ;ÿvÿRÿtÿ/ÿÿÿ#·Àÿ–aaÿs ÿ^ÿ]ÿ\ÿ[ÿZÿ[˜têrÿ\ ÿŸ©ÿ#%ÿÿÿ,/ÿš£ÿagÿÿÿÿÿHMÿ!…ÿ`ÿ`ÿ_ÿ]ÿ\ÿ\û\&uÑSÿ ÀÌÿovÿ.1ÿªµÿÈÕÿâðÿÈÕÿàîÿÓàÿ»Æÿž¨ÿQVÿ ÿ­¸ÿ/hnÿaÿ`ÿ_ÿ^ÿ_ÈvÄ$hoÿ²½ÿ KPÿÜêÿàîÿàîÿrîõÿD·¾ÿàîÿàîÿàîÿàîÿàîÿ•žÿbhÿ¼Èÿ8 "ÿbÿaÿ`ÿ`ÄxÀIFIÿZ£¨ÿ¬·ÿÜêÿßíÿáîÿåûýÿD·¾ÿàîÿàîÿàîÿ¸ÄÿÔáÿ¿Ëÿ~†ÿÂÎÿ?ÿdÿcÿaÿbÅzÃrÿtMNÿ‘ÿKµ¼ÿkÅËÿeæîÿúþþÿOÁÈÿàîÿàîÿàîÿ\ºÀÿ—ÂÅÿp»ÀÿФ¦ÿ?ÿfÿeÿdÿcÿdÇ{Ðzÿyÿ±llÿÿÿÿÿúúúÿ9¼Åÿúþþÿ}ÇÌÿàîÿàîÿàîÿœ¸ºÿÿÿÿÿÿÿÿÿÿþþÿµ„„ÿhÿgÿfÿeÿfÉ}ç|ÿ…ÿùôôÿÿÿÿÿÿÿÿÿ^­²ÿ»öúÿ¤ÐÓÿàîÿàîÿ¸Âÿðððÿÿÿÿÿÿÿÿÿÿÿÿÿüûûÿ†11ÿiÿhÿfÿhÉ€ù}ÿ´mmÿÿÿÿÿÿÿÿÿÿÿÿÿáääÿCÁÉÿ¾ÍÎÿÜéÿßíÿ‡²µÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÉ££ÿjÿiÿhÿhÅ€ÞÿÑ££ÿÿÿÿÿÿÿÿÿuuuÿSSSÿÆ×Øÿ[ËÒÿÎÛÿR²¸ÿÎÎÎÿÿ¶¶¶ÿÿÿÿÿÿÿÿÿÿÿÿÿòééÿlÿkÿjÿj¤–‚£ÿݼ¼ÿÿÿÿÿÿÿÿÿuuuÿSSSÿÿÿÿÿûüüÿãèèÿþþþÿáááÿBBBÿÇÇÇÿÿÿÿÿÿÿÿÿÿÿÿÿüùùÿmÿmÿkÿmdƒQ‚ÿÖ««ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿæÒÒÿoÿnÿnûm#„„ÛÀ~~ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÄ””ÿqÿpÿoÛn…W˜((þþýýÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþþÿšFFÿsÿrûpU††¢Ï››ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿúõõÿÞ¾¾ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÝÀÀÿxÿtÿt’‡‹ ËØ­­ÿÿÿÿÿÿÿÿÿÿÿÿÿ÷îîÿ£FFÿƒÿÌššÿüøøÿÿÿÿÿÿÿÿÿâÉÉÿ†ÿwÿvžt‡‡££==þ¾wwÿ´ccÿ‹ÿÿÿÿŠÿš88ÿ—66ÿÿzûzvx‡‡X†Ü†ÿ„ÿƒÿ‚ÿÿ€ÿÿ~þ}Â|7‡‡Q†•…ĄɃȂÁ…€8~ÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿ€ÿÿÿÿÿþÿÿÿÿÿüÿÿÿÿðÿÿÿÁÿÿþÿÿüÿÿøÿÿðÿÿàÿÿÀÿÿ€ÿÿÿÿÿþÿþÿþÿþÿþÿþÿÿÿÿÿÿÿÿàÿÿðÿÿðÿÿøÿÿàÿÿàÿÿÀÿÿÀÿÿÀÿÀÿÿÀÿÿàÿÿà?ÿÿð?ÿÿøÿÿÿ€ÿÿÿÿÀÿÿÿÿàÿÿÿÿàÿÿÿÿàÿÿÿÿðÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿà?ÿÿÿÿàÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿð?ÿÿÿÿøÿÿÿÿøÿÿÿÿÿüÿÿÿÿÿþÿÿÿÿÿÿÿÿÿÿÿÿÀÿÿÿÿÿÿÿÿÿÿÿopenmsx-0.10.0/src/PlugException.hh0000644000175000017500000000045612262345041017747 0ustar manuelmanuel00000000000000#ifndef PLUGEXCEPTION_HH #define PLUGEXCEPTION_HH #include "MSXException.hh" namespace openmsx { /** Thrown when a plug action fails. */ class PlugException : public MSXException { public: explicit PlugException(string_ref message) : MSXException(message) {} }; } // namespace openmsx #endif openmsx-0.10.0/src/RealTime.hh0000644000175000017500000000412412262345041016657 0ustar manuelmanuel00000000000000#ifndef MSXREALTIME_HH #define MSXREALTIME_HH #include "Schedulable.hh" #include "EventListener.hh" #include "Observer.hh" #include "EmuTime.hh" #include namespace openmsx { class MSXMotherBoard; class GlobalSettings; class EventDistributor; class EventDelay; class IntegerSetting; class BooleanSetting; class ThrottleManager; class Setting; class RealTime : private Schedulable, private EventListener, private Observer, private Observer { public: explicit RealTime( MSXMotherBoard& motherBoard, GlobalSettings& globalSettings, EventDelay& eventDelay); ~RealTime(); /** Convert EmuTime to RealTime. */ double getRealDuration(EmuTime::param time1, EmuTime::param time2); /** Convert RealTime to EmuTime. */ EmuDuration getEmuDuration(double realDur); /** Check that there is enough real time left before we reach as certain * point in emulated time. * @param us Real time duration is micro seconds. * @param time Point in emulated time. */ bool timeLeft(uint64_t us, EmuTime::param time); void resync(); void enable(); void disable(); private: /** Synchronize EmuTime with RealTime. * @param time The current emulation time. * @param allowSleep Is this method allowed to sleep, typically the * result of a previous call to timeLeft() is passed. */ void sync(EmuTime::param time, bool allowSleep); // Schedulable virtual void executeUntil(EmuTime::param time, int userData); // EventListener virtual int signalEvent(const std::shared_ptr& event); // Observer void update(const Setting& setting); // Observer void update(const ThrottleManager& throttleManager); void internalSync(EmuTime::param time, bool allowSleep); MSXMotherBoard& motherBoard; EventDistributor& eventDistributor; EventDelay& eventDelay; ThrottleManager& throttleManager; IntegerSetting& speedSetting; BooleanSetting& pauseSetting; BooleanSetting& powerSetting; uint64_t idealRealTime; EmuTime emuTime; double sleepAdjust; bool enabled; }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXTurboRPause.hh0000644000175000017500000000254412262345041017764 0ustar manuelmanuel00000000000000/* * This class implements the 2 Turbo-R specific LEDS: * * Bit 0 of IO-port 0xA7 turns the PAUSE led ON (1) or OFF (0) * Bit 7 TURBO * TODO merge doc below */ #ifndef TURBORPAUSE_HH #define TURBORPAUSE_HH #include "MSXDevice.hh" #include "Observer.hh" #include namespace openmsx { class BooleanSetting; class Setting; /** * This class implements the MSX Turbo-R pause key * * Whenever the pause key is pressed a flip-flop is toggled. * The status of this flip-flop can be read from io-port 0xA7. * bit 0 indicates the status (1 = pause active) * all other bits read 0 */ class MSXTurboRPause : public MSXDevice, private Observer { public: explicit MSXTurboRPause(const DeviceConfig& config); virtual ~MSXTurboRPause(); virtual void reset(EmuTime::param time); virtual void powerDown(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: // Observer void update(const Setting& setting); void updatePause(); const std::unique_ptr pauseSetting; byte status; bool pauseLed; bool turboLed; bool hwPause; }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXS1990.cc0000644000175000017500000000567212262345041016271 0ustar manuelmanuel00000000000000#include "MSXS1990.hh" #include "MSXCPU.hh" #include "MSXMotherBoard.hh" #include "PanasonicMemory.hh" #include "FirmwareSwitch.hh" #include "SimpleDebuggable.hh" #include "serialize.hh" #include "unreachable.hh" #include "memory.hh" namespace openmsx { class S1990Debuggable : public SimpleDebuggable { public: S1990Debuggable(MSXMotherBoard& motherBoard, MSXS1990& s1990); virtual byte read(unsigned address); virtual void write(unsigned address, byte value); private: MSXS1990& s1990; }; MSXS1990::MSXS1990(const DeviceConfig& config) : MSXDevice(config) , firmwareSwitch(make_unique(config)) , debuggable(make_unique(getMotherBoard(), *this)) { reset(EmuTime::dummy()); } MSXS1990::~MSXS1990() { } void MSXS1990::reset(EmuTime::param /*time*/) { registerSelect = 0; // TODO check this setCPUStatus(96); } byte MSXS1990::readIO(word port, EmuTime::param time) { return peekIO(port, time); } byte MSXS1990::peekIO(word port, EmuTime::param /*time*/) const { switch (port & 0x01) { case 0: return registerSelect; case 1: return readRegister(registerSelect); default: // unreachable, avoid warning UNREACHABLE; return 0; } } void MSXS1990::writeIO(word port, byte value, EmuTime::param /*time*/) { switch (port & 0x01) { case 0: registerSelect = value; break; case 1: writeRegister(registerSelect, value); break; default: UNREACHABLE; } } byte MSXS1990::readRegister(byte reg) const { PRT_DEBUG("S1990: read reg " << int(reg)); switch (reg) { case 5: return firmwareSwitch->getStatus() ? 0x40 : 0x00; case 6: return cpuStatus; case 13: return 0x03; // TODO case 14: return 0x2F; // TODO case 15: return 0x8B; // TODO default: return 0xFF; } } void MSXS1990::writeRegister(byte reg, byte value) { switch (reg) { case 6: setCPUStatus(value); break; } } void MSXS1990::setCPUStatus(byte value) { cpuStatus = value & 0x60; getCPU().setActiveCPU((cpuStatus & 0x20) ? MSXCPU::CPU_Z80 : MSXCPU::CPU_R800); bool dram = (cpuStatus & 0x40) ? false : true; getCPU().setDRAMmode(dram); getMotherBoard().getPanasonicMemory().setDRAM(dram); // TODO bit 7 -> reset MSX ????? } S1990Debuggable::S1990Debuggable(MSXMotherBoard& motherBoard, MSXS1990& s1990_) : SimpleDebuggable(motherBoard, s1990_.getName() + " regs", "S1990 registers", 16) , s1990(s1990_) { } byte S1990Debuggable::read(unsigned address) { return s1990.readRegister(address); } void S1990Debuggable::write(unsigned address, byte value) { s1990.writeRegister(address, value); } template void MSXS1990::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("registerSelect", registerSelect); ar.serialize("cpuStatus", cpuStatus); if (ar.isLoader()) { setCPUStatus(cpuStatus); } } INSTANTIATE_SERIALIZE_METHODS(MSXS1990); REGISTER_MSXDEVICE(MSXS1990, "S1990"); } // namespace openmsx openmsx-0.10.0/src/ReverseManager.hh0000644000175000017500000000671612262345041020074 0ustar manuelmanuel00000000000000#ifndef REVERSEMANGER_HH #define REVERSEMANGER_HH #include "Schedulable.hh" #include "EventListener.hh" #include "StateChangeListener.hh" #include "EmuTime.hh" #include "MemBuffer.hh" #include #include #include #include namespace openmsx { class MSXMotherBoard; class Keyboard; class EventDelay; class EventDistributor; class ReverseCmd; class TclObject; class ReverseManager : private Schedulable, private EventListener , private StateChangeRecorder { public: ReverseManager(MSXMotherBoard& motherBoard); ~ReverseManager(); // Keyboard is special because we need to transfer the host keyboard // state on 'reverse goto' to be able to resynchronize when replay // stops. See Keyboard::transferHostKeyMatrix() for more info. void registerKeyboard(Keyboard& keyboard); // To not loose any events we need to flush delayed events before // switching machine. See comments in goTo() for more info. void registerEventDelay(EventDelay& eventDelay); // Should only be used by MSXMotherBoard to be able to transfer // reRecordCount to ReverseManager for version 2 of MSXMotherBoard // serializers. void setReRecordCount(unsigned reRecordCount); private: struct ReverseChunk { ReverseChunk(); ReverseChunk(ReverseChunk&& rhs); ReverseChunk& operator=(ReverseChunk&& rhs); EmuTime time; MemBuffer savestate; // Number of recorded events (or replay index) when this // snapshot was created. So when going back replay should // start at this index. unsigned eventCount; }; typedef std::map Chunks; typedef std::vector> Events; struct ReverseHistory { void swap(ReverseHistory& other); void clear(); unsigned getNextSeqNum(EmuTime::param time) const; Chunks chunks; Events events; }; bool isCollecting() const; void start(); void stop(); void status(TclObject& result) const; void debugInfo(TclObject& result) const; void goBack(const std::vector& tokens); void goTo(const std::vector& tokens); void saveReplay(const std::vector& tokens, TclObject& result); void loadReplay(const std::vector& tokens, TclObject& result); void signalStopReplay(EmuTime::param time); EmuTime::param getEndTime(const ReverseHistory& history) const; void goTo(EmuTime::param targetTime, bool novideo); void goTo(EmuTime::param targetTime, bool novideo, ReverseHistory& history, bool sameTimeLine); void transferHistory(ReverseHistory& oldHistory, unsigned oldEventCount); void transferState(MSXMotherBoard& newBoard); void takeSnapshot(EmuTime::param time); void schedule(EmuTime::param time); void replayNextEvent(); template void dropOldSnapshots(unsigned count); // Schedulable virtual void executeUntil(EmuTime::param time, int userData); // EventListener virtual int signalEvent(const std::shared_ptr& event); // StateChangeRecorder virtual void signalStateChange(const std::shared_ptr& event); virtual void stopReplay(EmuTime::param time); virtual bool isReplaying() const; MSXMotherBoard& motherBoard; EventDistributor& eventDistributor; const std::unique_ptr reverseCmd; Keyboard* keyboard; EventDelay* eventDelay; ReverseHistory history; unsigned replayIndex; bool collecting; bool pendingTakeSnapshot; unsigned reRecordCount; friend class ReverseCmd; friend struct Replay; }; } // namespace openmsx #endif openmsx-0.10.0/src/Scheduler.cc0000644000175000017500000001153012262345041017060 0ustar manuelmanuel00000000000000#include "Scheduler.hh" #include "Schedulable.hh" #include "Thread.hh" #include "MSXCPU.hh" #include "serialize.hh" #include #include #include // for back_inserter namespace openmsx { struct LessSyncPoint { bool operator()(EmuTime::param time, const SynchronizationPoint& sp) const; bool operator()(const SynchronizationPoint& sp, EmuTime::param time) const; bool operator()(const SynchronizationPoint& lhs, const SynchronizationPoint& rhs) const; }; bool LessSyncPoint::operator()( EmuTime::param time, const SynchronizationPoint& sp) const { return time < sp.getTime(); } bool LessSyncPoint::operator()( const SynchronizationPoint& sp, EmuTime::param time) const { return sp.getTime() < time; } bool LessSyncPoint::operator()( const SynchronizationPoint& lhs, const SynchronizationPoint& rhs) const { // This method is needed for VC++ debug build (I'm not sure why). return lhs.getTime() < rhs.getTime(); } struct FindSchedulable { explicit FindSchedulable(const Schedulable& schedulable_) : schedulable(schedulable_) {} bool operator()(const SynchronizationPoint& sp) const { return sp.getDevice() == &schedulable; } const Schedulable& schedulable; }; struct EqualSchedulable { EqualSchedulable(const Schedulable& schedulable_, int userdata_) : schedulable(schedulable_), userdata(userdata_) {} bool operator()(const SynchronizationPoint& sp) const { return (sp.getDevice() == &schedulable) && (sp.getUserData() == userdata); } const Schedulable& schedulable; int userdata; }; Scheduler::Scheduler() : scheduleTime(EmuTime::zero) , cpu(nullptr) , scheduleInProgress(false) { } Scheduler::~Scheduler() { assert(!cpu); auto copy = syncPoints; for (auto& s : copy) { s.getDevice()->schedulerDeleted(); } assert(syncPoints.empty()); } void Scheduler::setSyncPoint(EmuTime::param time, Schedulable& device, int userData) { assert(Thread::isMainThread()); assert(time >= scheduleTime); // Push sync point into queue. auto it = std::upper_bound(syncPoints.begin(), syncPoints.end(), time, LessSyncPoint()); syncPoints.insert(it, SynchronizationPoint(time, &device, userData)); if (!scheduleInProgress && cpu) { // only when scheduleHelper() is not being executed // otherwise getNext() doesn't return the correct time and // scheduleHelper() anyway calls setNextSyncPoint() at the end cpu->setNextSyncPoint(getNext()); } } Scheduler::SyncPoints Scheduler::getSyncPoints(const Schedulable& device) const { SyncPoints result; copy_if(syncPoints.begin(), syncPoints.end(), back_inserter(result), FindSchedulable(device)); return result; } bool Scheduler::removeSyncPoint(Schedulable& device, int userData) { assert(Thread::isMainThread()); auto it = find_if(syncPoints.begin(), syncPoints.end(), EqualSchedulable(device, userData)); if (it != syncPoints.end()) { syncPoints.erase(it); return true; } else { return false; } } void Scheduler::removeSyncPoints(Schedulable& device) { assert(Thread::isMainThread()); syncPoints.erase(remove_if(syncPoints.begin(), syncPoints.end(), FindSchedulable(device)), syncPoints.end()); } bool Scheduler::pendingSyncPoint(const Schedulable& device, int userData) const { assert(Thread::isMainThread()); return find_if(syncPoints.begin(), syncPoints.end(), EqualSchedulable(device, userData)) != syncPoints.end(); } EmuTime::param Scheduler::getCurrentTime() const { assert(Thread::isMainThread()); return scheduleTime; } void Scheduler::scheduleHelper(EmuTime::param limit) { assert(!scheduleInProgress); scheduleInProgress = true; while (true) { // Get next sync point. const auto& sp = syncPoints.front(); EmuTime time = sp.getTime(); if (time > limit) { break; } assert(scheduleTime <= time); scheduleTime = time; Schedulable* device = sp.getDevice(); assert(device); int userData = sp.getUserData(); syncPoints.erase(syncPoints.begin()); device->executeUntil(time, userData); } scheduleInProgress = false; cpu->setNextSyncPoint(getNext()); } template void SynchronizationPoint::serialize(Archive& ar, unsigned /*version*/) { // SynchronizationPoint is always serialized via Schedulable. A // Schedulable has a collection of SynchronizationPoints, all with the // same Schedulable. So there's no need to serialize 'device'. //Schedulable* device; ar.serialize("time", timeStamp); ar.serialize("type", userData); } INSTANTIATE_SERIALIZE_METHODS(SynchronizationPoint); template void Scheduler::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("currentTime", scheduleTime); // don't serialize syncPoints, each Schedulable serializes its own // syncpoints } INSTANTIATE_SERIALIZE_METHODS(Scheduler); } // namespace openmsx openmsx-0.10.0/src/DebugDevice.hh0000644000175000017500000000173412262345041017327 0ustar manuelmanuel00000000000000#ifndef DEBUGDEVICE_HH #define DEBUGDEVICE_HH #include "MSXDevice.hh" #include #include namespace openmsx { class FilenameSetting; class DebugDevice : public MSXDevice { public: explicit DebugDevice(const DeviceConfig& config); virtual ~DebugDevice(); virtual void reset(EmuTime::param time); virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); // public for serialization enum DebugMode {OFF, SINGLEBYTE, MULTIBYTE, ASCII}; private: enum DisplayType {HEX, BIN, DEC, ASC}; void outputSingleByte(byte value, EmuTime::param time); void outputMultiByte(byte value); void displayByte(byte value, DisplayType type); void openOutput(const std::string& name); std::unique_ptr fileNameSetting; std::ostream* outputstrm; std::ofstream debugOut; std::string fileNameString; DebugMode mode; byte modeParameter; }; } // namespace openmsx #endif openmsx-0.10.0/src/MSXCielTurbo.hh0000644000175000017500000000115012262345041017431 0ustar manuelmanuel00000000000000#ifndef MSXCIELTURBO_HH #define MSXCIELTURBO_HH #include "MSXDevice.hh" #include namespace openmsx { class MSXCielTurbo : public MSXDevice { public: explicit MSXCielTurbo(const DeviceConfig& config); virtual ~MSXCielTurbo(); // MSXDevice virtual void reset(EmuTime::param time); virtual byte readIO(word port, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; virtual void writeIO(word port, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: byte lastValue; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/0000755000175000017500000000000012262345041016434 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/commands/MSXCommandController.cc0000644000175000017500000001374412262345041022766 0ustar manuelmanuel00000000000000#include "MSXCommandController.hh" #include "GlobalCommandController.hh" #include "Reactor.hh" #include "MSXEventDistributor.hh" #include "MSXMotherBoard.hh" #include "SettingsConfig.hh" #include "SettingsManager.hh" #include "InfoCommand.hh" #include "Interpreter.hh" #include "Setting.hh" #include "Event.hh" #include "MSXException.hh" #include "memory.hh" #include using std::string; using std::vector; namespace openmsx { MSXCommandController::MSXCommandController( GlobalCommandController& globalCommandController_, Reactor& reactor_, MSXMotherBoard& motherboard_, MSXEventDistributor& msxEventDistributor_, const std::string& machineID_) : globalCommandController(globalCommandController_) , reactor(reactor_) , motherboard(motherboard_) , msxEventDistributor(msxEventDistributor_) , machineID(machineID_) { globalCommandController.getInterpreter().createNamespace(machineID); machineInfoCommand = make_unique(*this, "machine_info"); machineInfoCommand->setAllowedInEmptyMachine(true); msxEventDistributor.registerEventListener(*this); } MSXCommandController::~MSXCommandController() { msxEventDistributor.unregisterEventListener(*this); machineInfoCommand.reset(); #ifndef NDEBUG for (auto& p : commandMap) { std::cout << "Command not unregistered: " << p.first() << std::endl; } for (auto& p : settingMap) { std::cout << "Setting not unregistered: " << p.first() << std::endl; } assert(commandMap.empty()); assert(settingMap.empty()); #endif globalCommandController.getInterpreter().deleteNamespace(machineID); } GlobalCommandController& MSXCommandController::getGlobalCommandController() { return globalCommandController; } InfoCommand& MSXCommandController::getMachineInfoCommand() { return *machineInfoCommand; } MSXMotherBoard& MSXCommandController::getMSXMotherBoard() const { return motherboard; } string MSXCommandController::getFullName(string_ref name) { return "::" + machineID + "::" + name; } void MSXCommandController::registerCommand(Command& command, const string& str) { assert(!hasCommand(str)); commandMap[str] = &command; string fullname = getFullName(str); globalCommandController.registerCommand(command, fullname); globalCommandController.registerProxyCommand(str); command.setAllowedInEmptyMachine(false); } void MSXCommandController::unregisterCommand(Command& command, string_ref str) { assert(hasCommand(str)); commandMap.erase(str); globalCommandController.unregisterProxyCommand(str); string fullname = getFullName(str); globalCommandController.unregisterCommand(command, fullname); } void MSXCommandController::registerCompleter(CommandCompleter& completer, string_ref str) { string fullname = getFullName(str); globalCommandController.registerCompleter(completer, fullname); } void MSXCommandController::unregisterCompleter(CommandCompleter& completer, string_ref str) { string fullname = getFullName(str); globalCommandController.unregisterCompleter(completer, fullname); } void MSXCommandController::registerSetting(Setting& setting) { const string& name = setting.getName(); assert(!findSetting(name)); settingMap[name] = &setting; globalCommandController.registerProxySetting(setting); string fullname = getFullName(name); globalCommandController.getSettingsConfig().getSettingsManager() .registerSetting(setting, fullname); globalCommandController.getInterpreter().registerSetting(setting, fullname); } void MSXCommandController::unregisterSetting(Setting& setting) { const string& name = setting.getName(); assert(findSetting(name)); settingMap.erase(name); globalCommandController.unregisterProxySetting(setting); string fullname = getFullName(name); globalCommandController.getInterpreter().unregisterSetting(setting, fullname); globalCommandController.getSettingsConfig().getSettingsManager() .unregisterSetting(setting, fullname); } void MSXCommandController::changeSetting(Setting& setting, const string& value) { string fullname = getFullName(setting.getName()); globalCommandController.changeSetting(fullname, value); } Command* MSXCommandController::findCommand(string_ref name) const { auto it = commandMap.find(name); return (it != commandMap.end()) ? it->second : nullptr; } BaseSetting* MSXCommandController::findSetting(string_ref name) { auto it = settingMap.find(name); return (it != settingMap.end()) ? it->second : nullptr; } const BaseSetting* MSXCommandController::findSetting(string_ref setting) const { return const_cast(this)->findSetting(setting); } bool MSXCommandController::hasCommand(string_ref command) const { return findCommand(command) != nullptr; } string MSXCommandController::executeCommand(const string& command, CliConnection* connection) { return globalCommandController.executeCommand(command, connection); } vector MSXCommandController::splitList(const string& list) { return globalCommandController.splitList(list); } CliComm& MSXCommandController::getCliComm() { return motherboard.getMSXCliComm(); } Interpreter& MSXCommandController::getInterpreter() { return globalCommandController.getInterpreter(); } void MSXCommandController::signalEvent( const std::shared_ptr& event, EmuTime::param /*time*/) { if (event->getType() != OPENMSX_MACHINE_ACTIVATED) return; // simple way to synchronize proxy settings for (auto& p : settingMap) { try { changeSetting(*p.second, p.second->getString()); } catch (MSXException&) { // ignore } } } bool MSXCommandController::isActive() const { return reactor.getMotherBoard() == &motherboard; } void MSXCommandController::transferSettings(const MSXCommandController& from) { for (auto& p : settingMap) { if (auto* fromSetting = from.findSetting(p.first())) { if (!fromSetting->needTransfer()) continue; try { changeSetting(*p.second, fromSetting->getString()); } catch (MSXException&) { // ignore } } } } } // namespace openmsx openmsx-0.10.0/src/commands/Command.cc0000644000175000017500000000451612262345041020327 0ustar manuelmanuel00000000000000#include "Command.hh" #include "CommandController.hh" #include "GlobalCommandController.hh" #include "MSXCommandController.hh" #include "TclObject.hh" #include "checked_cast.hh" #include "unreachable.hh" using std::vector; using std::string; namespace openmsx { // class CommandCompleter CommandCompleter::CommandCompleter(CommandController& commandController_, string_ref name) : Completer(name) , commandController(commandController_) { if (!getName().empty()) { getCommandController().registerCompleter(*this, getName()); } } CommandCompleter::~CommandCompleter() { if (!getName().empty()) { getCommandController().unregisterCompleter(*this, getName()); } } // TODO: getCommandController(), getGlobalCommandController() and // getInterpreter() occur both here and in Setting. CommandController& CommandCompleter::getCommandController() const { return commandController; } GlobalCommandController& CommandCompleter::getGlobalCommandController() const { if (auto globalCommandController = dynamic_cast(&commandController)) { return *globalCommandController; } else { return checked_cast(&commandController) ->getGlobalCommandController(); } } Interpreter& CommandCompleter::getInterpreter() const { return getGlobalCommandController().getInterpreter(); } CliComm& CommandCompleter::getCliComm() const { return getCommandController().getCliComm(); } // class Command Command::Command(CommandController& commandController, string_ref name) : CommandCompleter(commandController, name) , allowInEmptyMachine(true) { if (!getName().empty()) { getCommandController().registerCommand(*this, getName()); } } Command::~Command() { if (!getName().empty()) { getCommandController().unregisterCommand(*this, getName()); } } void Command::execute(const vector& tokens, TclObject& result) { vector strings; strings.reserve(tokens.size()); for (auto& t : tokens) { strings.push_back(t.getString().str()); } result.setString(execute(strings)); } string Command::execute(const vector& /*tokens*/) { // either this method or the method above should be reimplemented // by the subclasses UNREACHABLE; return ""; } void Command::tabCompletion(vector& /*tokens*/) const { // do nothing } } // namespace openmsx openmsx-0.10.0/src/commands/TclObject.hh0000644000175000017500000000532112262345041020627 0ustar manuelmanuel00000000000000#ifndef TCLOBJECT_HH #define TCLOBJECT_HH #include "string_ref.hh" #include "openmsx.hh" #include struct Tcl_Interp; struct Tcl_Obj; namespace openmsx { class Interpreter; class TclObject { public: TclObject(Tcl_Interp* interp, Tcl_Obj* object); TclObject(Tcl_Interp* interp, string_ref value); TclObject(Interpreter& interp, string_ref value); explicit TclObject(string_ref value); explicit TclObject(Tcl_Interp* interp); explicit TclObject(Interpreter& interp); TclObject(const TclObject& object); TclObject(); ~TclObject(); // assignment operator so we can use vector TclObject& operator=(const TclObject& other); // get associated interpreter Tcl_Interp* getInterpreter() const; // get underlying Tcl_Obj Tcl_Obj* getTclObject(); // value setters void setString(string_ref value); void setInt(int value); void setBoolean(bool value); void setDouble(double value); void setBinary(byte* buf, unsigned length); void addListElement(string_ref element); void addListElement(int value); void addListElement(double value); void addListElement(const TclObject& element); template void addListElements(ITER begin, ITER end); template void addListElements(const CONT& container); // value getters string_ref getString() const; int getInt() const; bool getBoolean() const; double getDouble() const; const byte* getBinary(unsigned& length) const; unsigned getListLength() const; TclObject getListIndex(unsigned index) const; TclObject getDictValue(const TclObject& key) const; // expressions bool evalBool() const; /** Interpret this TclObject as a command and execute it. * @param compile Should the command be compiled to bytecode? The * bytecode is stored inside the TclObject can speed up * future invocations of the same command. Only set this * flag when the command will be executed more than once. * TODO return TclObject instead of string? */ std::string executeCommand(bool compile = false); /** Comparison. Only compares the 'value', not the interpreter. */ bool operator==(const TclObject& other) const { return getString() == other.getString(); } bool operator!=(const TclObject& other) const { return !(*this == other); } private: void init(Tcl_Obj* obj_); void throwException() const; void addListElement(Tcl_Obj* element); Tcl_Interp* interp; Tcl_Obj* obj; }; template void TclObject::addListElements(ITER begin, ITER end) { for (ITER it = begin; it != end; ++it) { addListElement(*it); } } template void TclObject::addListElements(const CONT& container) { addListElements(std::begin(container), std::end(container)); } } // namespace openmsx #endif openmsx-0.10.0/src/commands/TclCallback.hh0000644000175000017500000000161412262345041021116 0ustar manuelmanuel00000000000000#ifndef TCLCALLBACK_HH #define TCLCALLBACK_HH #include "noncopyable.hh" #include "string_ref.hh" #include namespace openmsx { class CommandController; class Setting; class StringSetting; class TclObject; class TclCallback : private noncopyable { public: TclCallback(CommandController& controller, string_ref name, string_ref description, bool useCliComm = true, bool save = true); TclCallback(StringSetting& setting); ~TclCallback(); void execute(); void execute(int arg1, int arg2); void execute(int arg1, string_ref arg2); void execute(string_ref arg1, string_ref arg2); std::string getValue() const; StringSetting& getSetting() const; private: void executeCommon(TclObject& command); std::unique_ptr callbackSetting2; StringSetting& callbackSetting; const bool useCliComm; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/node.mk0000644000175000017500000000047512262345041017720 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ CommandException \ GlobalCommandController \ MSXCommandController \ ProxyCommand \ Completer \ Command \ InfoCommand \ InfoTopic \ Interpreter \ TclObject \ TclParser \ TclCallback \ HDR_ONLY:= \ InterpreterOutput \ CommandController include build/node-end.mk openmsx-0.10.0/src/commands/TclObject.cc0000644000175000017500000001217012262345041020615 0ustar manuelmanuel00000000000000#include "TclObject.hh" #include "Interpreter.hh" #include "CommandException.hh" #include #include using std::string; namespace openmsx { // class TclObject TclObject::TclObject(Tcl_Interp* interp_, Tcl_Obj* obj_) : interp(interp_) { init(obj_); } TclObject::TclObject(Tcl_Interp* interp_, string_ref value) : interp(interp_) { init(Tcl_NewStringObj(value.data(), int(value.size()))); } TclObject::TclObject(Interpreter& interp_, string_ref value) : interp(interp_.interp) { init(Tcl_NewStringObj(value.data(), int(value.size()))); } TclObject::TclObject(string_ref value) : interp(nullptr) { init(Tcl_NewStringObj(value.data(), int(value.size()))); } TclObject::TclObject(Tcl_Interp* interp_) : interp(interp_) { init(Tcl_NewObj()); } TclObject::TclObject(Interpreter& interp_) : interp(interp_.interp) { init(Tcl_NewObj()); } TclObject::TclObject(const TclObject& object) : interp(object.interp) { init(object.obj); } TclObject::TclObject() : interp(nullptr) { init(Tcl_NewObj()); } void TclObject::init(Tcl_Obj* obj_) { obj = obj_; Tcl_IncrRefCount(obj); } TclObject::~TclObject() { Tcl_DecrRefCount(obj); } TclObject& TclObject::operator=(const TclObject& other) { if (&other != this) { Tcl_DecrRefCount(obj); interp = other.interp; init(other.obj); } return *this; } Tcl_Interp* TclObject::getInterpreter() const { return interp; } Tcl_Obj* TclObject::getTclObject() { return obj; } void TclObject::throwException() const { string_ref message = interp ? Tcl_GetStringResult(interp) : "TclObject error"; throw CommandException(message); } void TclObject::setString(string_ref value) { if (Tcl_IsShared(obj)) { Tcl_DecrRefCount(obj); obj = Tcl_NewStringObj(value.data(), int(value.size())); Tcl_IncrRefCount(obj); } else { Tcl_SetStringObj(obj, value.data(), int(value.size())); } } void TclObject::setInt(int value) { if (Tcl_IsShared(obj)) { Tcl_DecrRefCount(obj); obj = Tcl_NewIntObj(value); Tcl_IncrRefCount(obj); } else { Tcl_SetIntObj(obj, value); } } void TclObject::setBoolean(bool value) { if (Tcl_IsShared(obj)) { Tcl_DecrRefCount(obj); obj = Tcl_NewBooleanObj(value); Tcl_IncrRefCount(obj); } else { Tcl_SetBooleanObj(obj, value); } } void TclObject::setDouble(double value) { if (Tcl_IsShared(obj)) { Tcl_DecrRefCount(obj); obj = Tcl_NewDoubleObj(value); Tcl_IncrRefCount(obj); } else { Tcl_SetDoubleObj(obj, value); } } void TclObject::setBinary(byte* buf, unsigned length) { if (Tcl_IsShared(obj)) { Tcl_DecrRefCount(obj); obj = Tcl_NewByteArrayObj(buf, length); Tcl_IncrRefCount(obj); } else { Tcl_SetByteArrayObj(obj, buf, length); } } void TclObject::addListElement(string_ref element) { addListElement(Tcl_NewStringObj(element.data(), int(element.size()))); } void TclObject::addListElement(int value) { addListElement(Tcl_NewIntObj(value)); } void TclObject::addListElement(double value) { addListElement(Tcl_NewDoubleObj(value)); } void TclObject::addListElement(const TclObject& element) { addListElement(element.obj); } void TclObject::addListElement(Tcl_Obj* element) { if (Tcl_IsShared(obj)) { Tcl_DecrRefCount(obj); obj = Tcl_DuplicateObj(obj); Tcl_IncrRefCount(obj); } if (Tcl_ListObjAppendElement(interp, obj, element) != TCL_OK) { throwException(); } } int TclObject::getInt() const { int result; if (Tcl_GetIntFromObj(interp, obj, &result) != TCL_OK) { throwException(); } return result; } bool TclObject::getBoolean() const { int result; if (Tcl_GetBooleanFromObj(interp, obj, &result) != TCL_OK) { throwException(); } return result != 0; } double TclObject::getDouble() const { double result; if (Tcl_GetDoubleFromObj(interp, obj, &result) != TCL_OK) { throwException(); } return result; } string_ref TclObject::getString() const { int length; char* buf = Tcl_GetStringFromObj(obj, &length); return string_ref(buf, length); } const byte* TclObject::getBinary(unsigned& length) const { return static_cast(Tcl_GetByteArrayFromObj( obj, reinterpret_cast(&length))); } unsigned TclObject::getListLength() const { int result; if (Tcl_ListObjLength(interp, obj, &result) != TCL_OK) { throwException(); } return result; } TclObject TclObject::getListIndex(unsigned index) const { Tcl_Obj* element; if (Tcl_ListObjIndex(interp, obj, index, &element) != TCL_OK) { throwException(); } return element ? TclObject(interp, element) : TclObject(interp); } TclObject TclObject::getDictValue(const TclObject& key) const { Tcl_Obj* value; if (Tcl_DictObjGet(interp, obj, key.obj, &value) != TCL_OK) { throwException(); } return value ? TclObject(interp, value) : TclObject(interp); } bool TclObject::evalBool() const { int result; if (Tcl_ExprBooleanObj(interp, obj, &result) != TCL_OK) { throwException(); } return result != 0; } string TclObject::executeCommand(bool compile) { assert(interp); int flags = compile ? 0 : TCL_EVAL_DIRECT; int success = Tcl_EvalObjEx(interp, obj, flags); string result = Tcl_GetStringResult(interp); if (success != TCL_OK) { throw CommandException(result); } return result; } } // namespace openmsx openmsx-0.10.0/src/commands/Completer.cc0000644000175000017500000001150412262345041020676 0ustar manuelmanuel00000000000000#include "Completer.hh" #include "InterpreterOutput.hh" #include "FileContext.hh" #include "FileOperations.hh" #include "ReadDir.hh" #include "utf8_unchecked.hh" #include "stringsp.hh" #include "xrange.hh" #include using std::vector; using std::string; namespace openmsx { InterpreterOutput* Completer::output = nullptr; Completer::Completer(string_ref name_) : name(name_.str()) { } Completer::~Completer() { } const string& Completer::getName() const { return name; } static bool formatHelper(const vector& input, size_t columnLimit, vector& result) { size_t column = 0; auto it = input.begin(); do { size_t maxcolumn = column; for (size_t i = 0; (i < result.size()) && (it != input.end()); ++i, ++it) { auto curSize = utf8::unchecked::size(result[i]); result[i] += string(column - curSize, ' '); result[i] += it->str(); maxcolumn = std::max(maxcolumn, utf8::unchecked::size(result[i])); if (maxcolumn > columnLimit) return false; } column = maxcolumn + 2; } while (it != input.end()); return true; } static vector format(const vector& input, size_t columnLimit) { vector result; for (size_t lines = 1; lines < input.size(); ++lines) { result.assign(lines, string()); if (formatHelper(input, columnLimit, result)) { return result; } } for (auto& s : input) { result.push_back(s.str()); } return result; } bool Completer::equalHead(string_ref s1, string_ref s2, bool caseSensitive) { if (s2.size() < s1.size()) return false; if (caseSensitive) { return memcmp(s1.data(), s2.data(), s1.size()) == 0; } else { return strncasecmp(s1.data(), s2.data(), s1.size()) == 0; } } bool Completer::completeImpl(string& str, vector matches, bool caseSensitive) { for (auto& m : matches) { assert(equalHead(str, m, caseSensitive)); (void)m; } if (matches.empty()) { // no matching values return false; } if (matches.size() == 1) { // only one match str = matches.front().str(); return true; } // Sort and remove duplicates. // For efficiency it's best if the list doesn't contain duplicates to // start with. Though sometimes this is hard to avoid. E.g. when doing // filename completion + some extra allowed strings and one of these // extra strings is the same as one of the filenames. sort(matches.begin(), matches.end()); matches.erase(unique(matches.begin(), matches.end()), matches.end()); bool expanded = false; while (true) { auto it = matches.begin(); if (str.size() == it->size()) { // match is as long as first word goto out; // TODO rewrite this } // expand with one char and check all strings auto begin = it->begin(); auto end = begin + str.size(); utf8::unchecked::next(end); // one more utf8 char string_ref string2(begin, end); for (/**/; it != matches.end(); ++it) { if (!equalHead(string2, *it, caseSensitive)) { goto out; // TODO rewrite this } } // no conflict found str = string2.str(); expanded = true; } out: if (!expanded && output) { // print all possibilities for (auto& line : format(matches, output->getOutputColumns() - 1)) { output->output(line); } } return false; } void Completer::completeFileName(vector& tokens, const FileContext& context) { completeFileNameImpl(tokens, context, vector()); } void Completer::completeFileNameImpl(vector& tokens, const FileContext& context, vector matches) { string& filename = tokens.back(); filename = FileOperations::expandTilde(filename); filename = FileOperations::expandCurrentDirFromDrive(filename); string_ref basename = FileOperations::getBaseName(filename); vector paths; if (FileOperations::isAbsolutePath(filename)) { paths.push_back(""); } else { paths = context.getPaths(); } vector filenames; for (auto& p : paths) { string dirname = FileOperations::join(p, basename); ReadDir dir(FileOperations::getNativePath(dirname)); while (dirent* de = dir.getEntry()) { string name = FileOperations::join(dirname, de->d_name); if (FileOperations::exists(name)) { string nm = FileOperations::join(basename, de->d_name); if (FileOperations::isDirectory(name)) { nm += '/'; } nm = FileOperations::getConventionalPath(nm); if (equalHead(filename, nm, true)) { filenames.push_back(nm); } } } } for (auto& f : filenames) { matches.push_back(f); } bool t = completeImpl(filename, matches, true); if (t && !filename.empty() && (filename.back() != '/')) { // completed filename, start new token tokens.push_back(""); } } void Completer::setOutput(InterpreterOutput* output_) { output = output_; } } // namespace openmsx openmsx-0.10.0/src/commands/TclParser.hh0000644000175000017500000000273212262345041020660 0ustar manuelmanuel00000000000000#ifndef TCLPARSER_HH #define TCLPARSER_HH #include "string_ref.hh" #include #include #define DEBUG_TCLPARSER 0 class TclParser { public: /** Input: Tcl interpreter and command to parse */ TclParser(Tcl_Interp* interp, string_ref input); /** Ouput: a string of equal length of the input command where * each character indicates the type of the corresponding * character in the command. Currently the possible colors are: * 'E' -> error * 'c' -> comment * 'v' -> variable * 'l' -> literal (string or number) * 'p' -> proc * 'o' -> operator * '.' -> other */ std::string getColors() const { return colors; } /** Get Start of the last subcommand. This is the command that should * be completed by tab-completion. */ int getLast() const { return last.back(); } /** Is the given string a valid Tcl command. This also takes the * 'lazy' Tcl scripts into account. */ static bool isProc(Tcl_Interp* interp, string_ref str); private: enum ParseType { COMMAND, EXPRESSION, OTHER }; void parse(const char* p, int size, ParseType type); void printTokens(Tcl_Token* tokens, int numTokens); static ParseType guessSubType(Tcl_Token* tokens, int i); void setColors(const char* p, int size, char c); private: Tcl_Interp* interp; std::string colors; std::string parseStr; std::vector last; int offset; #if DEBUG_TCLPARSER void DEBUG_PRINT(const std::string& s); int level; #else #define DEBUG_PRINT(x) #endif }; #endif openmsx-0.10.0/src/commands/TclParser.cc0000644000175000017500000001513712262345041020651 0ustar manuelmanuel00000000000000#include "TclParser.hh" #include "ScopedAssign.hh" #include #include #include #include using std::string; #if DEBUG_TCLPARSER void TclParser::DEBUG_PRINT(const string& s) { std::cout << string(2 * level, ' ') << s << std::endl; } static string_ref type2string(int type) { switch (type) { case TCL_TOKEN_WORD: return "word"; case TCL_TOKEN_SIMPLE_WORD: return "simple word"; case TCL_TOKEN_EXPAND_WORD: return "expand word"; case TCL_TOKEN_TEXT: return "text"; case TCL_TOKEN_BS: return "bs"; case TCL_TOKEN_COMMAND: return "command"; case TCL_TOKEN_VARIABLE: return "variable"; case TCL_TOKEN_SUB_EXPR: return "sub expr"; case TCL_TOKEN_OPERATOR: return "operator"; default: assert(false); return ""; } } #endif static bool isNumber(string_ref str) { string_ref::size_type idx; stoll(str, &idx); return idx == str.size(); } TclParser::TclParser(Tcl_Interp* interp_, string_ref input) : interp(interp_) , colors(input.size(), '.') , parseStr(input.str()) , offset(0) #if DEBUG_TCLPARSER , level(0) #endif { parse(parseStr.data(), int(parseStr.size()), COMMAND); } void TclParser::parse(const char* p, int size, ParseType type) { ScopedAssign sa1(offset, offset + (p - parseStr.data())); ScopedAssign sa2(parseStr, string(p, size)); last.push_back(offset); // The functions Tcl_ParseCommand() and Tcl_ParseExpr() are meant to // operate on a complete command. For interactive syntax highlighting // we also want to pass incomplete commands (e.g. with an opening, but // not yet a closing brace). This loop tries to parse and depening on // the parse error retries with a completed command. Tcl_Parse parseInfo; int retryCount = 0; while (true) { int parseStatus = (type == EXPRESSION) ? Tcl_ParseExpr(interp, parseStr.data(), int(parseStr.size()), &parseInfo) : Tcl_ParseCommand(interp, parseStr.data(), int(parseStr.size()), 1, &parseInfo); if (parseStatus == TCL_OK) break; Tcl_FreeParse(&parseInfo); ++retryCount; bool allowComplete = ((offset + parseStr.size()) >= colors.size()) && (retryCount < 10); Tcl_Obj* resObj = Tcl_GetObjResult(interp); int resLen; const char* resStr = Tcl_GetStringFromObj(resObj, &resLen); string_ref error(resStr, resLen); if (allowComplete && error.starts_with("missing close-brace")) { parseStr += '}'; } else if (allowComplete && error.starts_with("missing close-bracket")) { parseStr += ']'; } else if (allowComplete && error.starts_with( "missing \"")) { parseStr += '"'; } else if (allowComplete && error.starts_with("unbalanced open paren")) { parseStr += ')'; } else if (allowComplete && error.starts_with("missing operand")) { // This also triggers for a (wrong) expression like // 'if { / 3' // and that can't be solved by adding something at the // end. Without the retryCount stuff we would get in an // infinte loop here. parseStr += '0'; } else if (allowComplete && error.starts_with("missing )")) { parseStr += ')'; } else { DEBUG_PRINT("ERROR: " + parseStr + ": " + error); setColors(parseStr.data(), int(parseStr.size()), 'E'); if ((offset + size) < int(colors.size())) last.pop_back(); return; } } if (type == EXPRESSION) { DEBUG_PRINT("EXPRESSION: " + parseStr); } else { if (parseInfo.commentSize) { DEBUG_PRINT("COMMENT: " + string_ref(parseInfo.commentStart, parseInfo.commentSize)); setColors(parseInfo.commentStart, parseInfo.commentSize, 'c'); } DEBUG_PRINT("COMMAND: " + string_ref(parseInfo.commandStart, parseInfo.commandSize)); } printTokens(parseInfo.tokenPtr, parseInfo.numTokens); // If the current sub-command stops before the end of the original // full command, then it's not the last sub-command. Note that // sub-commands can be nested. if ((offset + size) < int(colors.size())) last.pop_back(); const char* nextStart = parseInfo.commandStart + parseInfo.commandSize; Tcl_FreeParse(&parseInfo); if (type == COMMAND) { // next command int nextSize = int((parseStr.data() + parseStr.size()) - nextStart); if (nextSize > 0) { parse(nextStart, nextSize, type); } } } void TclParser::printTokens(Tcl_Token* tokens, int numTokens) { #if DEBUG_TCLPARSER ScopedAssign sa(level, level + 1); #endif for (int i = 0; i < numTokens; /**/) { Tcl_Token& token = tokens[i]; string_ref tokenStr(token.start, token.size); DEBUG_PRINT(type2string(token.type) + " -> " + tokenStr); switch (token.type) { case TCL_TOKEN_VARIABLE: assert(token.numComponents >= 1); setColors(tokens[i + 1].start - 1, tokens[i + 1].size + 1, 'v'); break; case TCL_TOKEN_WORD: case TCL_TOKEN_SIMPLE_WORD: if (*token.start == '"') { setColors(token.start, token.size, 'l'); } if ((i == 0) && isProc(interp, tokenStr)) { setColors(token.start, token.size, 'p'); } break; case TCL_TOKEN_EXPAND_WORD: setColors(token.start, 3, 'o'); break; case TCL_TOKEN_OPERATOR: case TCL_TOKEN_BS: setColors(token.start, token.size, 'o'); break; case TCL_TOKEN_TEXT: if (isNumber(tokenStr) || (*token.start == '"')) { // TODO only works if the same as 'l' setColors(token.start, token.size, 'l'); } break; } if (token.type == TCL_TOKEN_COMMAND) { parse(token.start + 1, token.size - 2, COMMAND); } else if (token.type == TCL_TOKEN_SIMPLE_WORD) { ParseType subType = guessSubType(tokens, i); if (subType != OTHER) { parse(tokens[i + 1].start, tokens[i + 1].size, subType); } } printTokens(&tokens[++i], token.numComponents); i += token.numComponents; } } TclParser::ParseType TclParser::guessSubType(Tcl_Token* tokens, int i) { // heuristic: if previous token is 'if' then assume this is an expression if ((i >= 1) && (tokens[i - 1].type == TCL_TOKEN_TEXT)) { string_ref prevText(tokens[i - 1].start, tokens[i - 1].size); if ((prevText == "if") || (prevText == "elseif") || (prevText == "expr")) { return EXPRESSION; } } // heuristic: parse text that starts with { as a subcommand if (*tokens[i].start == '{') { return COMMAND; } // a plain text element return OTHER; } bool TclParser::isProc(Tcl_Interp* interp, string_ref str) { string command = "openmsx::is_command_name {" + str + '}'; if (Tcl_Eval(interp, command.c_str()) != TCL_OK) return false; int result; if (Tcl_GetBooleanFromObj(interp, Tcl_GetObjResult(interp), &result) != TCL_OK) return false; return result != 0; } void TclParser::setColors(const char* p, int size, char c) { int start = (p - parseStr.data()) + offset; int stop = std::min(start + size, int(colors.size())); for (int i = start; i < stop; ++i) { colors[i] = c; } } openmsx-0.10.0/src/commands/TclCallback.cc0000644000175000017500000000461212262345041021105 0ustar manuelmanuel00000000000000#include "TclCallback.hh" #include "TclObject.hh" #include "GlobalCommandController.hh" #include "CliComm.hh" #include "CommandException.hh" #include "StringSetting.hh" #include "memory.hh" #include #include using std::string; namespace openmsx { TclCallback::TclCallback( CommandController& controller, string_ref name, string_ref description, bool useCliComm_, bool save) : callbackSetting2(make_unique( controller, name, description, "", save ? Setting::SAVE : Setting::DONT_SAVE)) , callbackSetting(*callbackSetting2) , useCliComm(useCliComm_) { } TclCallback::TclCallback(StringSetting& setting) : callbackSetting(setting) , useCliComm(true) { } TclCallback::~TclCallback() { } StringSetting& TclCallback::getSetting() const { return callbackSetting; } string TclCallback::getValue() const { return getSetting().getString(); } void TclCallback::execute() { const string callback = getValue(); if (callback.empty()) return; TclObject command(callbackSetting.getInterpreter()); command.addListElement(callback); executeCommon(command); } void TclCallback::execute(int arg1, int arg2) { const string callback = getValue(); if (callback.empty()) return; TclObject command(callbackSetting.getInterpreter()); command.addListElement(callback); command.addListElement(arg1); command.addListElement(arg2); executeCommon(command); } void TclCallback::execute(int arg1, string_ref arg2) { const string callback = getValue(); if (callback.empty()) return; TclObject command(callbackSetting.getInterpreter()); command.addListElement(callback); command.addListElement(arg1); command.addListElement(arg2); executeCommon(command); } void TclCallback::execute(string_ref arg1, string_ref arg2) { const string callback = getValue(); if (callback.empty()) return; TclObject command(callbackSetting.getInterpreter()); command.addListElement(callback); command.addListElement(arg1); command.addListElement(arg2); executeCommon(command); } void TclCallback::executeCommon(TclObject& command) { try { command.executeCommand(); } catch (CommandException& e) { string message = "Error executing callback function \"" + getSetting().getName() + "\": " + e.getMessage(); if (useCliComm) { getSetting().getCommandController().getCliComm().printWarning( message); } else { std::cerr << message << std::endl; } } } } // namespace openmsx openmsx-0.10.0/src/commands/InfoTopic.hh0000644000175000017500000000245312262345041020653 0ustar manuelmanuel00000000000000#ifndef INFOTOPIC_HH #define INFOTOPIC_HH #include "Completer.hh" #include #include namespace openmsx { class TclObject; class InfoCommand; class InfoTopic : public Completer { public: /** Show info on this topic * @param tokens Tokenized command line; * tokens[1] is the topic. * @param result The result of this topic must be assigned to this * parameter. * @throw CommandException Thrown when there was an error while * executing this InfoTopic. */ virtual void execute(const std::vector& tokens, TclObject& result) const = 0; /** Print help for this topic. * @param tokens Tokenized command line; * tokens[1] is the topic. */ virtual std::string help(const std::vector& tokens) const = 0; /** Attempt tab completion for this topic. * Default implementation does nothing. * @param tokens Tokenized command line; * tokens[1] is the topic. * The last token is incomplete, this method tries to complete it. */ virtual void tabCompletion(std::vector& tokens) const; protected: InfoTopic(InfoCommand& infoCommand, const std::string& name); virtual ~InfoTopic(); private: InfoCommand& infoCommand; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/InfoTopic.cc0000644000175000017500000000071612262345041020641 0ustar manuelmanuel00000000000000#include "InfoTopic.hh" #include "InfoCommand.hh" using std::string; using std::vector; namespace openmsx { InfoTopic::InfoTopic(InfoCommand& infoCommand_, const string& name) : Completer(name) , infoCommand(infoCommand_) { infoCommand.registerTopic(*this, getName()); } InfoTopic::~InfoTopic() { infoCommand.unregisterTopic(*this, getName()); } void InfoTopic::tabCompletion(vector& /*tokens*/) const { // do nothing } } // namespace openmsx openmsx-0.10.0/src/commands/GlobalCommandController.cc0000644000175000017500000004535512262345041023522 0ustar manuelmanuel00000000000000#include "GlobalCommandController.hh" #include "Command.hh" #include "Setting.hh" #include "ProxyCommand.hh" #include "ProxySetting.hh" #include "InfoTopic.hh" #include "LocalFileReference.hh" #include "GlobalCliComm.hh" #include "CliConnection.hh" #include "HotKey.hh" #include "Interpreter.hh" #include "InfoCommand.hh" #include "CommandException.hh" #include "SettingsConfig.hh" #include "SettingsManager.hh" #include "RomInfoTopic.hh" #include "TclObject.hh" #include "Version.hh" #include "ScopedAssign.hh" #include "StringOp.hh" #include "checked_cast.hh" #include "openmsx.hh" #include "memory.hh" #include "xrange.hh" #include #include using std::string; using std::vector; namespace openmsx { class HelpCmd : public Command { public: explicit HelpCmd(GlobalCommandController& controller); virtual void execute(const vector& tokens, TclObject& result); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: GlobalCommandController& controller; }; class TabCompletionCmd : public Command { public: explicit TabCompletionCmd(GlobalCommandController& controller); virtual void execute(const vector& tokens, TclObject& result); virtual string help(const vector& tokens) const; private: GlobalCommandController& controller; }; class UpdateCmd : public Command { public: explicit UpdateCmd(CommandController& commandController); virtual string execute(const vector& tokens); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: CliConnection& getConnection(); }; class PlatformInfo : public InfoTopic { public: explicit PlatformInfo(InfoCommand& openMSXInfoCommand); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; }; class VersionInfo : public InfoTopic { public: explicit VersionInfo(InfoCommand& openMSXInfoCommand); virtual void execute(const vector& tokens, TclObject& result) const; virtual string help(const vector& tokens) const; }; GlobalCommandController::GlobalCommandController( EventDistributor& eventDistributor, GlobalCliComm& cliComm_, Reactor& reactor_) : cliComm(cliComm_) , connection(nullptr) , reactor(reactor_) , interpreter(make_unique(eventDistributor)) , openMSXInfoCommand(make_unique(*this, "openmsx_info")) , hotKey(make_unique(*this, eventDistributor)) , helpCmd(make_unique(*this)) , tabCompletionCmd(make_unique(*this)) , proxyCmd(make_unique(*this, reactor)) , platformInfo(make_unique(getOpenMSXInfoCommand())) , versionInfo(make_unique(getOpenMSXInfoCommand())) , romInfoTopic(make_unique(getOpenMSXInfoCommand())) { // For backwards compatibility: // In the past we had an openMSX command 'update'. This was a mistake // because it overlaps with the native Tcl command with the same name. // We renamed 'update' to 'openmsx_update'. And installed a wrapper // around 'update' that either forwards to the native Tcl command or // to the 'openmsx_update' command. // In future openMSX versions this wrapper will be removed. interpreter->execute("rename update __tcl_update"); interpreter->execute( "proc update { args } {\n" " if {$args == \"\"} {\n" " __tcl_update\n" " } elseif {$args == \"idletasks\"} {\n" " __tcl_update idletasks\n" " } else {\n" " puts stderr \"Warning: the openMSX \\'update\\' command " "overlapped with a native Tcl command " "and has been renamed to \\'openmsx_update\\'. " "In future openMSX releases this forwarder " "will stop working, so please change your " "scripts to use the \\'openmsx_update\\' " "command instead of \\'update\\'.\"\n" " eval \"openmsx_update $args\"\n" " }\n" "}\n"); updateCmd = make_unique(*this); } GlobalCommandController::~GlobalCommandController() { // all this reset() stuff is also done automatically by the destructor, // but we need it slightly earlier to test the assertions. // TODO find a cleaner way to do this romInfoTopic.reset(); platformInfo.reset(); versionInfo.reset(); updateCmd.reset(); tabCompletionCmd.reset(); helpCmd.reset(); settingsConfig.reset(); hotKey.reset(); openMSXInfoCommand.reset(); assert(commands.empty()); assert(commandCompleters.empty()); } void GlobalCommandController::registerProxyCommand(const string& name) { if (proxyCommandMap[name] == 0) { registerCommand(*proxyCmd, name); registerCompleter(*proxyCmd, name); } ++proxyCommandMap[name]; } void GlobalCommandController::unregisterProxyCommand(string_ref name) { assert(proxyCommandMap[name]); --proxyCommandMap[name]; if (proxyCommandMap[name] == 0) { unregisterCompleter(*proxyCmd, name); unregisterCommand(*proxyCmd, name); } } GlobalCommandController::ProxySettings::iterator GlobalCommandController::findProxySetting(const std::string& name) { return find_if(proxySettings.begin(), proxySettings.end(), [&](ProxySettings::value_type& v) { return v.first->getName() == name; }); } void GlobalCommandController::registerProxySetting(Setting& setting) { const auto& name = setting.getName(); auto it = findProxySetting(name); if (it == proxySettings.end()) { // first occurrence auto proxy = make_unique(reactor, name); getSettingsConfig().getSettingsManager().registerSetting(*proxy, name); getInterpreter().registerSetting(*proxy, name); proxySettings.push_back(std::make_pair(std::move(proxy), 1)); } else { // was already registered ++(it->second); } } void GlobalCommandController::unregisterProxySetting(Setting& setting) { const auto& name = setting.getName(); auto it = findProxySetting(name); assert(it != proxySettings.end()); assert(it->second); --(it->second); if (it->second == 0) { auto& proxy = *it->first; getInterpreter().unregisterSetting(proxy, name); getSettingsConfig().getSettingsManager().unregisterSetting(proxy, name); proxySettings.erase(it); } } CliComm& GlobalCommandController::getCliComm() { return cliComm; } CliConnection* GlobalCommandController::getConnection() const { return connection; } Interpreter& GlobalCommandController::getInterpreter() { return *interpreter; } InfoCommand& GlobalCommandController::getOpenMSXInfoCommand() { return *openMSXInfoCommand; } SettingsConfig& GlobalCommandController::getSettingsConfig() { if (!settingsConfig) { settingsConfig = make_unique(*this, *hotKey); } return *settingsConfig; } void GlobalCommandController::registerCommand( Command& command, const string& str) { assert(commands.find(str) == commands.end()); commands[str] = &command; interpreter->registerCommand(str, command); } void GlobalCommandController::unregisterCommand( Command& command, string_ref str) { assert(commands.find(str) != commands.end()); assert(commands.find(str)->second == &command); interpreter->unregisterCommand(str, command); commands.erase(str); } void GlobalCommandController::registerCompleter( CommandCompleter& completer, string_ref str) { assert(commandCompleters.find(str) == commandCompleters.end()); commandCompleters[str] = &completer; } void GlobalCommandController::unregisterCompleter( CommandCompleter& completer, string_ref str) { (void)completer; assert(commandCompleters.find(str) != commandCompleters.end()); assert(commandCompleters.find(str)->second == &completer); commandCompleters.erase(str); } void GlobalCommandController::registerSetting(Setting& setting) { const auto& name = setting.getName(); getSettingsConfig().getSettingsManager().registerSetting(setting, name); interpreter->registerSetting(setting, name); } void GlobalCommandController::unregisterSetting(Setting& setting) { const auto& name = setting.getName(); interpreter->unregisterSetting(setting, name); getSettingsConfig().getSettingsManager().unregisterSetting(setting, name); } BaseSetting* GlobalCommandController::findSetting(string_ref name) { return getSettingsConfig().getSettingsManager().findSetting(name); } void GlobalCommandController::changeSetting( const std::string& name, const string& value) { interpreter->setVariable(name, value); } void GlobalCommandController::changeSetting(Setting& setting, const string& value) { changeSetting(setting.getName(), value); } bool GlobalCommandController::hasCommand(string_ref command) const { return commands.find(command) != commands.end(); } void GlobalCommandController::split(string_ref str, vector& tokens, const char delimiter) { enum ParseState {Alpha, BackSlash, Quote}; ParseState state = Alpha; for (auto chr : str) { switch (state) { case Alpha: if (tokens.empty()) { tokens.push_back(""); } if (chr == delimiter) { // token done, start new token tokens.push_back(""); } else { tokens.back() += chr; if (chr == '\\') { state = BackSlash; } else if (chr == '"') { state = Quote; } } break; case Quote: tokens.back() += chr; if (chr == '"') { state = Alpha; } break; case BackSlash: tokens.back() += chr; state = Alpha; break; } } } string GlobalCommandController::removeEscaping(const string& str) { enum ParseState {Alpha, BackSlash, Quote}; ParseState state = Alpha; string result; for (auto chr : str) { switch (state) { case Alpha: if (chr == '\\') { state = BackSlash; } else if (chr == '"') { state = Quote; } else { result += chr; } break; case Quote: if (chr == '"') { state = Alpha; } else { result += chr; } break; case BackSlash: result += chr; state = Alpha; break; } } return result; } vector GlobalCommandController::removeEscaping( const vector& input, bool keepLastIfEmpty) { vector result; for (auto& s : input) { if (!s.empty()) { result.push_back(removeEscaping(s)); } } if (keepLastIfEmpty && (input.empty() || input.back().empty())) { result.push_back(""); } return result; } static string escapeChars(const string& str, const string& chars) { string result; for (auto chr : str) { if (chars.find(chr) != string::npos) { result += '\\'; } result += chr; } return result; } string GlobalCommandController::addEscaping(const string& str, bool quote, bool finished) { if (str.empty() && finished) { quote = true; } string result = escapeChars(str, "$[]"); if (quote) { result = '"' + result; if (finished) { result += '"'; } } else { result = escapeChars(result, " "); } return result; } string GlobalCommandController::join( const vector& tokens, char delimiter) { StringOp::Builder result; bool first = true; for (auto& t : tokens) { if (!first) { result << delimiter; } first = false; result << t; } return result; } bool GlobalCommandController::isComplete(const string& command) { return interpreter->isComplete(command); } string GlobalCommandController::executeCommand( const string& cmd, CliConnection* connection_) { ScopedAssign sa(connection, connection_); return interpreter->execute(cmd); } vector GlobalCommandController::splitList(const string& list) { return interpreter->splitList(list); } void GlobalCommandController::source(const string& script) { try { LocalFileReference file(script); interpreter->executeFile(file.getFilename()); } catch (CommandException& e) { getCliComm().printWarning( "While executing " + script + ": " + e.getMessage()); } } string GlobalCommandController::tabCompletion(string_ref command) { // split on 'active' command (the command that should actually be // completed). Some examples: // if {[debug rea <-- should complete the 'debug' command // instead of the 'if' command // bind F6 { cycl <-- should complete 'cycle' instead of 'bind' TclParser parser = interpreter->parse(command); int last = parser.getLast(); string_ref pre = command.substr(0, last); string_ref post = command.substr(last); // split command string in tokens vector originalTokens; split(post, originalTokens, ' '); if (originalTokens.empty()) { originalTokens.push_back(""); } // complete last token auto tokens = removeEscaping(originalTokens, true); auto oldNum = tokens.size(); tabCompletion(tokens); auto newNum = tokens.size(); bool tokenFinished = oldNum != newNum; // replace last token string& original = originalTokens.back(); string& completed = tokens[oldNum - 1]; if (!completed.empty()) { bool quote = !original.empty() && (original[0] == '"'); original = addEscaping(completed, quote, tokenFinished); } if (tokenFinished) { assert(newNum == (oldNum + 1)); assert(tokens.back().empty()); originalTokens.push_back(""); } // rebuild command string return pre + join(originalTokens, ' '); } void GlobalCommandController::tabCompletion(vector& tokens) { if (tokens.empty()) { // nothing typed yet return; } if (tokens.size() == 1) { // build a list of all command strings Completer::completeString(tokens, interpreter->getCommandNames()); } else { auto it = commandCompleters.find(tokens.front()); if (it != commandCompleters.end()) { it->second->tabCompletion(tokens); } else { TclObject command(*interpreter); command.addListElement("openmsx::tabcompletion"); command.addListElements(tokens); try { auto list = splitList(command.executeCommand()); bool sensitive = true; if (!list.empty()) { if (list.back() == "false") { list.pop_back(); sensitive = false; } else if (list.back() == "true") { list.pop_back(); sensitive = true; } } Completer::completeString(tokens, list, sensitive); } catch (CommandException& e) { cliComm.printWarning( "Error while executing tab-completion " "proc: " + e.getMessage()); } } } } // Help Command HelpCmd::HelpCmd(GlobalCommandController& controller_) : Command(controller_, "help") , controller(controller_) { } void HelpCmd::execute(const vector& tokens, TclObject& result) { switch (tokens.size()) { case 1: { string text = "Use 'help [command]' to get help for a specific command\n" "The following commands exist:\n"; for (auto& p : controller.commandCompleters) { const auto& key = p.first(); text.append(key.data(), key.size()); text += '\n'; } result.setString(text); break; } default: { auto it = controller.commandCompleters.find(tokens[1].getString()); if (it != controller.commandCompleters.end()) { vector tokens2; auto it2 = tokens.begin(); for (++it2; it2 != tokens.end(); ++it2) { tokens2.push_back(it2->getString().str()); } result.setString(it->second->help(tokens2)); } else { TclObject command(result.getInterpreter()); command.addListElement("openmsx::help"); command.addListElements(tokens.begin() + 1, tokens.end()); result.setString(command.executeCommand()); } break; } } } string HelpCmd::help(const vector& /*tokens*/) const { return "prints help information for commands\n"; } void HelpCmd::tabCompletion(vector& tokens) const { string front = tokens.front(); tokens.erase(tokens.begin()); controller.tabCompletion(tokens); tokens.insert(tokens.begin(), front); } // TabCompletionCmd Command TabCompletionCmd::TabCompletionCmd(GlobalCommandController& controller_) : Command(controller_, "tabcompletion") , controller(controller_) { } void TabCompletionCmd::execute(const vector& tokens, TclObject& result) { switch (tokens.size()) { case 2: { // TODO this prints list of possible completions in the console result.setString(controller.tabCompletion(tokens[1].getString())); break; } default: throw SyntaxError(); } } string TabCompletionCmd::help(const vector& /*tokens*/) const { return "!!! This command will change in the future !!!\n" "Tries to completes the given argument as if it were typed in " "the console. This command is only useful to provide " "tabcompletion to external console interfaces."; } // class UpdateCmd UpdateCmd::UpdateCmd(CommandController& commandController) : Command(commandController, "openmsx_update") { } static GlobalCliComm::UpdateType getType(const string& name) { auto updateStr = CliComm::getUpdateStrings(); for (auto i : xrange(updateStr.size())) { if (updateStr[i] == name) { return static_cast(i); } } throw CommandException("No such update type: " + name); } CliConnection& UpdateCmd::getConnection() { auto* controller = checked_cast( &getCommandController()); if (auto* connection = controller->getConnection()) { return *connection; } throw CommandException("This command only makes sense when " "it's used from an external application."); } string UpdateCmd::execute(const vector& tokens) { if (tokens.size() != 3) { throw SyntaxError(); } if (tokens[1] == "enable") { getConnection().setUpdateEnable(getType(tokens[2]), true); } else if (tokens[1] == "disable") { getConnection().setUpdateEnable(getType(tokens[2]), false); } else { throw SyntaxError(); } return ""; } string UpdateCmd::help(const vector& /*tokens*/) const { static const string helpText = "Enable or disable update events for external applications. See doc/openmsx-control-xml.txt."; return helpText; } void UpdateCmd::tabCompletion(vector& tokens) const { switch (tokens.size()) { case 2: { static const char* const ops[] = { "enable", "disable" }; completeString(tokens, ops); break; } case 3: completeString(tokens, CliComm::getUpdateStrings()); break; } } // Platform info PlatformInfo::PlatformInfo(InfoCommand& openMSXInfoCommand) : InfoTopic(openMSXInfoCommand, "platform") { } void PlatformInfo::execute(const vector& /*tokens*/, TclObject& result) const { result.setString(TARGET_PLATFORM); } string PlatformInfo::help(const vector& /*tokens*/) const { return "Prints openMSX platform."; } // Version info VersionInfo::VersionInfo(InfoCommand& openMSXInfoCommand) : InfoTopic(openMSXInfoCommand, "version") { } void VersionInfo::execute(const vector& /*tokens*/, TclObject& result) const { result.setString(Version::full()); } string VersionInfo::help(const vector& /*tokens*/) const { return "Prints openMSX version."; } } // namespace openmsx openmsx-0.10.0/src/commands/CommandException.hh0000644000175000017500000000047612262345041022221 0ustar manuelmanuel00000000000000#ifndef COMMANDEXCEPTION_HH #define COMMANDEXCEPTION_HH #include "MSXException.hh" namespace openmsx { class CommandException : public MSXException { public: explicit CommandException(string_ref message); }; class SyntaxError : public CommandException { public: SyntaxError(); }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/InterpreterOutput.hh0000644000175000017500000000045312262345041022503 0ustar manuelmanuel00000000000000#ifndef INTERPRETEROUTPUT_HH #define INTERPRETEROUTPUT_HH #include "string_ref.hh" namespace openmsx { class InterpreterOutput { public: virtual ~InterpreterOutput() {} virtual void output(string_ref text) = 0; virtual unsigned getOutputColumns() const = 0; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/InfoCommand.cc0000644000175000017500000000506012262345041021136 0ustar manuelmanuel00000000000000#include "InfoCommand.hh" #include "InfoTopic.hh" #include "TclObject.hh" #include "MSXCommandController.hh" #include "CliComm.hh" #include "CommandException.hh" #include "MSXMotherBoard.hh" #include "KeyRange.hh" #include "unreachable.hh" #include #include using std::string; using std::vector; namespace openmsx { InfoCommand::InfoCommand(CommandController& commandController, const string& name) : Command(commandController, name) { } InfoCommand::~InfoCommand() { assert(infoTopics.empty()); } void InfoCommand::registerTopic(InfoTopic& topic, string_ref name) { #ifndef NDEBUG if (infoTopics.find(name) != infoTopics.end()) { std::cerr << "INTERNAL ERROR: already have a info topic with " "name " << name << std::endl; UNREACHABLE; } #endif infoTopics[name] = &topic; } void InfoCommand::unregisterTopic(InfoTopic& topic, string_ref name) { (void)topic; if (infoTopics.find(name) == infoTopics.end()) { std::cerr << "INTERNAL ERROR: can't unregister topic with name " "name " << name << ", not found!" << std::endl; UNREACHABLE; } assert(infoTopics[name] == &topic); infoTopics.erase(name); } // Command void InfoCommand::execute(const vector& tokens, TclObject& result) { switch (tokens.size()) { case 1: // list topics for (auto& p : infoTopics) { result.addListElement(p.first()); } break; default: // show info about topic assert(tokens.size() >= 2); const auto& topic = tokens[1].getString(); auto it = infoTopics.find(topic); if (it == infoTopics.end()) { throw CommandException("No info on: " + topic); } it->second->execute(tokens, result); break; } } string InfoCommand::help(const vector& tokens) const { string result; switch (tokens.size()) { case 1: // show help on info cmd result = "Show info on a certain topic\n" " info [topic] [...]\n"; break; default: // show help on a certain topic assert(tokens.size() >= 2); auto it = infoTopics.find(tokens[1]); if (it == infoTopics.end()) { throw CommandException("No info on: " + tokens[1]); } result = it->second->help(tokens); break; } return result; } void InfoCommand::tabCompletion(vector& tokens) const { switch (tokens.size()) { case 2: { // complete topic completeString(tokens, keys(infoTopics)); break; } default: // show help on a certain topic assert(tokens.size() >= 3); auto it = infoTopics.find(tokens[1]); if (it != infoTopics.end()) { it->second->tabCompletion(tokens); } break; } } } // namespace openmsx openmsx-0.10.0/src/commands/CommandException.cc0000644000175000017500000000034512262345041022202 0ustar manuelmanuel00000000000000#include "CommandException.hh" namespace openmsx { CommandException::CommandException(string_ref message) : MSXException(message) { } SyntaxError::SyntaxError() : CommandException("Syntax error") { } } // namespace openmsx openmsx-0.10.0/src/commands/Command.hh0000644000175000017500000000407712262345041020343 0ustar manuelmanuel00000000000000#ifndef COMMAND_HH #define COMMAND_HH #include "Completer.hh" #include "string_ref.hh" #include namespace openmsx { class CommandController; class GlobalCommandController; class Interpreter; class TclObject; class CliComm; class CommandCompleter : public Completer { public: CommandController& getCommandController() const; protected: CommandCompleter(CommandController& commandController, string_ref name); virtual ~CommandCompleter(); GlobalCommandController& getGlobalCommandController() const; Interpreter& getInterpreter() const; CliComm& getCliComm() const; private: CommandController& commandController; }; class Command : public CommandCompleter { public: /** Execute this command. * @param tokens Tokenized command line; * tokens[0] is the command itself. * @param result The result of the command must be assigned to this * parameter. * @throws CommandException Thrown when there was an error while * executing this command. */ virtual void execute(const std::vector& tokens, TclObject& result); /** Alternative for the execute() method above. * It has a simpler interface, but performance is a bit lower. * Subclasses should override either this method or the one above. */ virtual std::string execute(const std::vector& tokens); /** Attempt tab completion for this command. * Default implementation does nothing. * @param tokens Tokenized command line; * tokens[0] is the command itself. * The last token is incomplete, this method tries to complete it. */ virtual void tabCompletion(std::vector& tokens) const; // see comments in MSXMotherBoard::loadMachineCommand void setAllowedInEmptyMachine(bool value) { allowInEmptyMachine = value; } bool isAllowedInEmptyMachine() const { return allowInEmptyMachine; } protected: Command(CommandController& commandController, string_ref name); virtual ~Command(); private: bool allowInEmptyMachine; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/GlobalCommandController.hh0000644000175000017500000000752712262345041023533 0ustar manuelmanuel00000000000000#ifndef GLOBALCOMMANDCONTROLLER_HH #define GLOBALCOMMANDCONTROLLER_HH #include "CommandController.hh" #include "StringMap.hh" #include "noncopyable.hh" #include #include #include namespace openmsx { class EventDistributor; class Reactor; class GlobalCliComm; class HotKey; class InfoCommand; class Interpreter; class HelpCmd; class TabCompletionCmd; class UpdateCmd; class ProxyCmd; class PlatformInfo; class VersionInfo; class RomInfoTopic; class ProxySetting; class SettingsConfig; class GlobalCommandController : public CommandController, private noncopyable { public: GlobalCommandController(EventDistributor& eventDistributor, GlobalCliComm& cliComm, Reactor& reactor); ~GlobalCommandController(); InfoCommand& getOpenMSXInfoCommand(); /** * Executes all defined auto commands */ void source(const std::string& script); void registerProxyCommand(const std::string& name); void unregisterProxyCommand(string_ref name); void registerProxySetting(Setting& setting); void unregisterProxySetting(Setting& setting); void changeSetting(const std::string& name, const std::string& value); // CommandController virtual void registerCompleter(CommandCompleter& completer, string_ref str); virtual void unregisterCompleter(CommandCompleter& completer, string_ref str); virtual void registerCommand(Command& command, const std::string& str); virtual void unregisterCommand(Command& command, string_ref str); virtual bool hasCommand(string_ref command) const; virtual std::string executeCommand(const std::string& command, CliConnection* connection = nullptr); /** * Complete the given command. */ virtual std::string tabCompletion(string_ref command); /** * Returns true iff the command is complete (all braces, quotes etc. are * balanced). */ virtual bool isComplete(const std::string& command); virtual std::vector splitList(const std::string& list); virtual void registerSetting(Setting& setting); virtual void unregisterSetting(Setting& setting); virtual BaseSetting* findSetting(string_ref name); virtual void changeSetting(Setting& setting, const std::string& value); virtual CliComm& getCliComm(); virtual Interpreter& getInterpreter(); virtual SettingsConfig& getSettingsConfig(); virtual CliConnection* getConnection() const; private: void split(string_ref str, std::vector& tokens, char delimiter); std::string join(const std::vector& tokens, char delimiter); std::string removeEscaping(const std::string& str); std::vector removeEscaping( const std::vector& input, bool keepLastIfEmpty); std::string addEscaping(const std::string& str, bool quote, bool finished); void tabCompletion(std::vector& tokens); typedef std::vector, unsigned>> ProxySettings; ProxySettings::iterator findProxySetting(const std::string& name); StringMap commands; StringMap commandCompleters; GlobalCliComm& cliComm; CliConnection* connection; Reactor& reactor; std::unique_ptr interpreter; std::unique_ptr openMSXInfoCommand; std::unique_ptr hotKey; std::unique_ptr settingsConfig; friend class HelpCmd; std::unique_ptr helpCmd; std::unique_ptr tabCompletionCmd; std::unique_ptr updateCmd; std::unique_ptr proxyCmd; std::unique_ptr platformInfo; std::unique_ptr versionInfo; std::unique_ptr romInfoTopic; StringMap proxyCommandMap; ProxySettings proxySettings; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/ProxyCommand.cc0000644000175000017500000000303412262345041021363 0ustar manuelmanuel00000000000000#include "ProxyCommand.hh" #include "MSXCommandController.hh" #include "TclObject.hh" #include "CommandException.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" #include "checked_cast.hh" using std::vector; using std::string; namespace openmsx { ProxyCmd::ProxyCmd(CommandController& controller, Reactor& reactor_) : Command(controller, "") , reactor(reactor_) { } Command* ProxyCmd::getMachineCommand(string_ref name) const { MSXMotherBoard* motherBoard = reactor.getMotherBoard(); if (!motherBoard) return nullptr; return motherBoard->getMSXCommandController().findCommand(name); } void ProxyCmd::execute(const vector& tokens, TclObject& result) { string_ref name = tokens[0].getString(); if (Command* command = getMachineCommand(name)) { if (!command->isAllowedInEmptyMachine()) { auto controller = checked_cast( &command->getCommandController()); if (!controller->getMSXMotherBoard().getMachineConfig()) { throw CommandException( "Can't execute command in empty machine"); } } command->execute(tokens, result); } else { throw CommandException("Invalid command name \"" + name + '"'); } } string ProxyCmd::help(const vector& tokens) const { if (Command* command = getMachineCommand(tokens[0])) { return command->help(tokens); } else { return "unknown command: " + tokens[0]; } } void ProxyCmd::tabCompletion(vector& tokens) const { if (Command* command = getMachineCommand(tokens[0])) { command->tabCompletion(tokens); } } } // namespace openmsx openmsx-0.10.0/src/commands/Completer.hh0000644000175000017500000000620412262345041020711 0ustar manuelmanuel00000000000000#ifndef COMPLETER_HH #define COMPLETER_HH #include "noncopyable.hh" #include "inline.hh" #include "string_ref.hh" #include namespace openmsx { class FileContext; class InterpreterOutput; class Completer : private noncopyable { public: const std::string& getName() const; /** Print help for this command. */ virtual std::string help(const std::vector& tokens) const = 0; /** Attempt tab completion for this command. * @param tokens Tokenized command line; * tokens[0] is the command itself. * The last token is incomplete, this method tries to complete it. */ virtual void tabCompletion(std::vector& tokens) const = 0; template static void completeString(std::vector& tokens, const RANGE& possibleValues, bool caseSensitive = true); template static void completeFileName(std::vector& tokens, const FileContext& context, const RANGE& extra); static void completeFileName(std::vector& tokens, const FileContext& context); // should only be called by CommandConsole static void setOutput(InterpreterOutput* output); protected: explicit Completer(string_ref name); virtual ~Completer(); private: static bool equalHead(string_ref s1, string_ref s2, bool caseSensitive); template static std::vector filter( string_ref str, ITER begin, ITER end, bool caseSensitive); template static std::vector filter( string_ref str, const RANGE& range, bool caseSensitive); static bool completeImpl(std::string& str, std::vector matches, bool caseSensitive); static void completeFileNameImpl(std::vector& tokens, const FileContext& context, std::vector matches); const std::string name; static InterpreterOutput* output; }; template NEVER_INLINE std::vector Completer::filter( string_ref str, ITER begin, ITER end, bool caseSensitive) { std::vector result; for (auto it = begin; it != end; ++it) { if (equalHead(str, *it, caseSensitive)) { result.push_back(*it); } } return result; } template inline std::vector Completer::filter( string_ref str, const RANGE& range, bool caseSensitive) { return filter(str, std::begin(range), std::end(range), caseSensitive); } template void Completer::completeString( std::vector& tokens, const RANGE& possibleValues, bool caseSensitive) { auto& str = tokens.back(); if (completeImpl(str, filter(str, possibleValues, caseSensitive), caseSensitive)) { tokens.push_back(""); } } template void Completer::completeFileName( std::vector& tokens, const FileContext& context, const RANGE& extra) { completeFileNameImpl(tokens, context, filter(tokens.back(), extra, true)); } } // namespace openmsx #endif openmsx-0.10.0/src/commands/MSXCommandController.hh0000644000175000017500000000536712262345041023002 0ustar manuelmanuel00000000000000#ifndef MSXCOMMANDCONTROLLER_HH #define MSXCOMMANDCONTROLLER_HH #include "CommandController.hh" #include "MSXEventListener.hh" #include "StringMap.hh" #include "noncopyable.hh" #include namespace openmsx { class GlobalCommandController; class Reactor; class MSXMotherBoard; class MSXEventDistributor; class InfoCommand; class MSXCommandController : public CommandController, private MSXEventListener, private noncopyable { public: MSXCommandController(GlobalCommandController& globalCommandController, Reactor& reactor, MSXMotherBoard& motherboard, MSXEventDistributor& msxEventDistributor, const std::string& machineID); ~MSXCommandController(); GlobalCommandController& getGlobalCommandController(); InfoCommand& getMachineInfoCommand(); MSXMotherBoard& getMSXMotherBoard() const; Command* findCommand(string_ref name) const; /** Returns true iff the machine this controller belongs to is currently * active. */ bool isActive() const; /** Transfer setting values from one machine to another, * used for during 'reverse'. */ void transferSettings(const MSXCommandController& from); // CommandController virtual void registerCompleter(CommandCompleter& completer, string_ref str); virtual void unregisterCompleter(CommandCompleter& completer, string_ref str); virtual void registerCommand(Command& command, const std::string& str); virtual void unregisterCommand(Command& command, string_ref str); virtual bool hasCommand(string_ref command) const; virtual std::string executeCommand(const std::string& command, CliConnection* connection = nullptr); virtual std::vector splitList(const std::string& list); virtual void registerSetting(Setting& setting); virtual void unregisterSetting(Setting& setting); virtual BaseSetting* findSetting(string_ref name); virtual void changeSetting(Setting& setting, const std::string& value); virtual CliComm& getCliComm(); virtual Interpreter& getInterpreter(); const BaseSetting* findSetting(string_ref setting) const; private: std::string getFullName(string_ref name); // MSXEventListener virtual void signalEvent(const std::shared_ptr& event, EmuTime::param time); GlobalCommandController& globalCommandController; Reactor& reactor; MSXMotherBoard& motherboard; MSXEventDistributor& msxEventDistributor; const std::string& machineID; std::unique_ptr machineInfoCommand; StringMap commandMap; StringMap settingMap; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/CommandController.hh0000644000175000017500000000321412262345041022377 0ustar manuelmanuel00000000000000#ifndef COMMANDCONTROLLER_HH #define COMMANDCONTROLLER_HH #include "string_ref.hh" #include namespace openmsx { class CommandCompleter; class Command; class CliConnection; class Setting; class BaseSetting; class CliComm; class Interpreter; class CommandController { public: /** * (Un)register a command completer, used to complete build-in Tcl cmds */ virtual void registerCompleter(CommandCompleter& completer, string_ref str) = 0; virtual void unregisterCompleter(CommandCompleter& completer, string_ref str) = 0; /** * (Un)register a command */ virtual void registerCommand(Command& command, const std::string& str) = 0; virtual void unregisterCommand(Command& command, string_ref str) = 0; /** * Does a command with this name already exist? */ virtual bool hasCommand(string_ref command) const = 0; /** * Execute the given command */ virtual std::string executeCommand(const std::string& command, CliConnection* connection = nullptr) = 0; virtual std::vector splitList(const std::string& list) = 0; /** TODO */ virtual void registerSetting(Setting& setting) = 0; virtual void unregisterSetting(Setting& setting) = 0; virtual BaseSetting* findSetting(string_ref name) = 0; virtual void changeSetting(Setting& setting, const std::string& value) = 0; virtual CliComm& getCliComm() = 0; virtual Interpreter& getInterpreter() = 0; protected: CommandController() {} virtual ~CommandController() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/Interpreter.hh0000644000175000017500000000464512262345041021271 0ustar manuelmanuel00000000000000#ifndef INTERPRETER_HH #define INTERPRETER_HH #include "TclParser.hh" #include "EventListener.hh" #include "StringMap.hh" #include "string_ref.hh" #include "noncopyable.hh" #include #include namespace openmsx { class EventDistributor; class Command; class BaseSetting; class InterpreterOutput; class TclObject; class Interpreter : private EventListener, private noncopyable { public: explicit Interpreter(EventDistributor& eventDistributor); ~Interpreter(); void setOutput(InterpreterOutput* output); void init(const char* programName); void registerCommand(const std::string& name, Command& command); void unregisterCommand(string_ref name, Command& command); std::vector getCommandNames(); bool isComplete(const std::string& command) const; std::string execute(const std::string& command); std::string executeFile(const std::string& filename); void setVariable(const std::string& name, const std::string& value); void unsetVariable(const std::string& name); const char* getVariable(const std::string& name) const; void registerSetting(BaseSetting& variable, const std::string& name); void unregisterSetting(BaseSetting& variable, const std::string& name); /** Create the global namespace with given name. * @param name Name of the namespace, should not include '::' prefix. */ void createNamespace(const std::string& name); /** Delete the global namespace with given name. * @param name Name of the namespace, should not include '::' prefix. */ void deleteNamespace(const std::string& name); std::vector splitList(const std::string& list); static std::vector splitList( const std::string& list, Tcl_Interp* interp); TclParser parse(string_ref command); private: // EventListener virtual int signalEvent(const std::shared_ptr& event); void poll(); static int outputProc(ClientData clientData, const char* buf, int toWrite, int* errorCodePtr); static int commandProc(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]); static char* traceProc(ClientData clientData, Tcl_Interp* interp, const char* part1, const char* part2, int flags); EventDistributor& eventDistributor; static Tcl_ChannelType channelType; Tcl_Interp* interp; StringMap commandTokenMap; InterpreterOutput* output; friend class TclObject; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/InfoCommand.hh0000644000175000017500000000134412262345041021151 0ustar manuelmanuel00000000000000#ifndef INFOCOMMAND_HH #define INFOCOMMAND_HH #include "Command.hh" #include "StringMap.hh" namespace openmsx { class InfoTopic; class InfoCommand : public Command { public: InfoCommand(CommandController& commandController, const std::string& name); virtual ~InfoCommand(); void registerTopic(InfoTopic& topic, string_ref name); void unregisterTopic(InfoTopic& topic, string_ref name); private: // Command virtual void execute(const std::vector& tokens, TclObject& result); virtual std::string help(const std::vector& tokens) const; virtual void tabCompletion(std::vector& tokens) const; StringMap infoTopics; }; } // namespace openmsx #endif openmsx-0.10.0/src/commands/Interpreter.cc0000644000175000017500000003323312262345041021252 0ustar manuelmanuel00000000000000#include "Interpreter.hh" #include "EventDistributor.hh" #include "Command.hh" #include "TclObject.hh" #include "CommandException.hh" #include "MSXCommandController.hh" #include "MSXMotherBoard.hh" #include "Setting.hh" #include "InterpreterOutput.hh" #include "MSXCPUInterface.hh" #include "FileOperations.hh" #include "unreachable.hh" #include "xrange.hh" #include #include //#include #include "openmsx.hh" using std::string; using std::vector; namespace openmsx { // See comments in traceProc() static std::map traceMap; static long traceCount = 0; static int dummyClose(ClientData /*instanceData*/, Tcl_Interp* /*interp*/) { return 0; } static int dummyInput(ClientData /*instanceData*/, char* /*buf*/, int /*bufSize*/, int* /*errorCodePtr*/) { return 0; } static void dummyWatch(ClientData /*instanceData*/, int /*mask*/) { } static int dummyGetHandle(ClientData /*instanceData*/, int /*direction*/, ClientData* /*handlePtr*/) { return TCL_ERROR; } Tcl_ChannelType Interpreter::channelType = { const_cast("openMSX console"),// Type name nullptr, // Always non-blocking dummyClose, // Close proc dummyInput, // Input proc Interpreter::outputProc, // Output proc nullptr, // Seek proc nullptr, // Set option proc nullptr, // Get option proc dummyWatch, // Watch for events on console dummyGetHandle, // Get a handle from the device nullptr, // Tcl_DriverClose2Proc nullptr, // Tcl_DriverBlockModeProc nullptr, // Tcl_DriverFlushProc nullptr, // Tcl_DriverHandlerProc nullptr, // Tcl_DriverWideSeekProc nullptr, // Tcl_DriverThreadActionProc nullptr, // Tcl_DriverTruncateProc }; void Interpreter::init(const char* programName) { Tcl_FindExecutable(programName); } Interpreter::Interpreter(EventDistributor& eventDistributor_) : eventDistributor(eventDistributor_) { interp = Tcl_CreateInterp(); Tcl_Preserve(interp); // TODO need to investigate this: doesn't work on windows /* if (Tcl_Init(interp) != TCL_OK) { std::cout << "Tcl_Init: " << interp->result << std::endl; } if (Tk_Init(interp) != TCL_OK) { std::cout << "Tk_Init error: " << interp->result << std::endl; } if (Tcl_Eval(interp, "wm withdraw .") != TCL_OK) { std::cout << "wm withdraw error: " << interp->result << std::endl; } */ Tcl_Channel channel = Tcl_CreateChannel(&channelType, "openMSX console", this, TCL_WRITABLE); if (channel) { Tcl_SetChannelOption(interp, channel, "-translation", "binary"); Tcl_SetChannelOption(interp, channel, "-buffering", "line"); Tcl_SetChannelOption(interp, channel, "-encoding", "utf-8"); } Tcl_SetStdChannel(channel, TCL_STDOUT); setVariable("env(OPENMSX_USER_DATA)", FileOperations::getUserDataDir()); setVariable("env(OPENMSX_SYSTEM_DATA)", FileOperations::getSystemDataDir()); eventDistributor.registerEventListener(OPENMSX_POLL_EVENT, *this); } Interpreter::~Interpreter() { // see comment in MSXCPUInterface::cleanup() MSXCPUInterface::cleanup(); eventDistributor.unregisterEventListener(OPENMSX_POLL_EVENT, *this); if (!Tcl_InterpDeleted(interp)) { Tcl_DeleteInterp(interp); } Tcl_Release(interp); Tcl_Finalize(); } void Interpreter::setOutput(InterpreterOutput* output_) { output = output_; } int Interpreter::outputProc(ClientData clientData, const char* buf, int toWrite, int* /*errorCodePtr*/) { try { auto* output = static_cast(clientData)->output; string_ref text(buf, toWrite); if (!text.empty() && output) { output->output(text); } } catch (...) { UNREACHABLE; // we cannot let exceptions pass through Tcl } return toWrite; } void Interpreter::registerCommand(const string& name, Command& command) { assert(commandTokenMap.find(name) == commandTokenMap.end()); commandTokenMap[name] = Tcl_CreateObjCommand( interp, name.c_str(), commandProc, static_cast(&command), nullptr); } void Interpreter::unregisterCommand(string_ref name, Command& /*command*/) { auto it = commandTokenMap.find(name); assert(it != commandTokenMap.end()); Tcl_DeleteCommandFromToken(interp, it->second); commandTokenMap.erase(it); } int Interpreter::commandProc(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { try { auto& command = *static_cast(clientData); vector tokens; tokens.reserve(objc); for (auto i : xrange(objc)) { tokens.push_back(TclObject(interp, objv[i])); } int res = TCL_OK; TclObject result(interp); try { if (!command.isAllowedInEmptyMachine()) { if (auto controller = dynamic_cast( &command.getCommandController())) { if (!controller->getMSXMotherBoard().getMachineConfig()) { throw CommandException( "Can't execute command in empty machine"); } } } command.execute(tokens, result); } catch (MSXException& e) { PRT_DEBUG( "Interpreter: Got an exception while executing a command: " << e.getMessage()); result.setString(e.getMessage()); res = TCL_ERROR; } Tcl_SetObjResult(interp, result.getTclObject()); return res; } catch (...) { UNREACHABLE; // we cannot let exceptions pass through Tcl return TCL_ERROR; } } // Returns // - build-in Tcl commands // - openmsx commands // - user-defined procs vector Interpreter::getCommandNames() { return splitList(execute("openmsx::all_command_names"), interp); } bool Interpreter::isComplete(const string& command) const { return Tcl_CommandComplete(command.c_str()) != 0; } string Interpreter::execute(const string& command) { int success = Tcl_Eval(interp, command.c_str()); string result = Tcl_GetStringResult(interp); if (success != TCL_OK) { throw CommandException(result); } return result; } string Interpreter::executeFile(const string& filename) { int success = Tcl_EvalFile(interp, filename.c_str()); string result = Tcl_GetStringResult(interp); if (success != TCL_OK) { throw CommandException(result); } return result; } static void setVar(Tcl_Interp* interp, const char* name, const char* value) { if (!Tcl_SetVar(interp, name, value, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)) { // might contain error message of a trace proc std::cerr << Tcl_GetStringResult(interp) << std::endl; } } static const char* getVar(Tcl_Interp* interp, const char* name) { return Tcl_GetVar(interp, name, TCL_GLOBAL_ONLY); } void Interpreter::setVariable(const string& name, const string& value) { if (!Tcl_SetVar(interp, name.c_str(), value.c_str(), TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG)) { throw CommandException(Tcl_GetStringResult(interp)); } } void Interpreter::unsetVariable(const string& name) { Tcl_UnsetVar(interp, name.c_str(), TCL_GLOBAL_ONLY); } const char* Interpreter::getVariable(const string& name) const { return getVar(interp, name.c_str()); } static string getSafeValueString(BaseSetting& setting) { try { return setting.getString(); } catch (MSXException&) { return "0"; // 'safe' value, see comment in registerSetting() } } void Interpreter::registerSetting(BaseSetting& variable, const string& name) { if (const char* tclVarValue = getVariable(name)) { // Tcl var already existed, use this value try { variable.setStringDirect(tclVarValue); } catch (MSXException&) { // Ignore: can happen in case of proxy settings when // the current machine doesn't have this setting. // E.g. // (start with cbios machine) // set renshaturbo 0 // create_machine // machine2::load_machine Panasonic_FS-A1GT } } else { // define Tcl var setVariable(name, getSafeValueString(variable)); } // The call setVariable() above can already trigger traces on this // variable (in Tcl it's possible to already set traces on a variable // before that variable is defined). We didn't yet set a trace on it // ourselves. So for example on proxy-settings we don't yet delegate // read/writes to the actual setting. This means that inside the trace // callback we see the value set above instead of the 'actual' value. // // This scenario can be triggered in the load_icons script by // executing the following commands (interactively): // delete_machine machine1 // create_machine // machine2::load_machine msx2 // // Before changing the 'safe-value' (see getSafeValueString()) to '0', // this gave errors because the load_icons script didn't expect to see // 'proxy' (the old 'safe-value') as value. // // The current solution (choosing '0' as safe value) is not ideal, but // good enough for now. // // A possible better solution is to move Tcl_TraceVar() before // setVariable(), I did an initial attempt but there were some // problems. TODO investigate this further. long traceID = traceCount++; traceMap[traceID] = &variable; Tcl_TraceVar(interp, name.c_str(), TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, traceProc, reinterpret_cast(traceID)); } void Interpreter::unregisterSetting(BaseSetting& variable, const string& name) { auto it = traceMap.begin(); while (true) { assert(it != traceMap.end()); if (it->second == &variable) break; ++it; } long traceID = it->first; Tcl_UntraceVar(interp, name.c_str(), TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, traceProc, reinterpret_cast(traceID)); traceMap.erase(it); unsetVariable(name); } static BaseSetting* getTraceSetting(unsigned traceID) { auto it = traceMap.find(traceID); return (it != traceMap.end()) ? it->second : nullptr; } char* Interpreter::traceProc(ClientData clientData, Tcl_Interp* interp, const char* part1, const char* /*part2*/, int flags) { try { // Lookup Setting object that belongs to this Tcl variable. // // In a previous implementation we passed this object directly // as the clientData. However this went wrong in the following // scenario: // // proc foo {} { carta eject ; carta spmanbow.rom } // bind Q foo // [press Q twice] // // The problem is that when a SCC cartridge is removed, we // delete several settings (e.g. SCC_ch1_mute). While deleting // a setting we unset the corresponsing Tcl variable (see // unregisterSetting() above), this in turn triggers a // TCL_TRACE_UNSET callback (this function). To prevent this // callback from triggering we first remove the trace before // unsetting the variable. However it seems when a setting is // deleted from within an active Tcl proc (like in the example // above), the callback is anyway triggered, but only at the // end of the proc (so in the foo proc above, the settings // are deleted after the first statement, but the callbacks // only happen after the second statement). By that time the // Setting object is already deleted and the callback function // works on a deleted object. // // To prevent this we don't anymore pass a pointer to the // Setting object as clientData, but we lookup the Setting in // a map. If the Setting was deleted, we won't find it anymore // in the map and return. auto traceID = reinterpret_cast(clientData); auto* variable = getTraceSetting(traceID); if (!variable) return nullptr; static string static_string; if (flags & TCL_TRACE_READS) { try { setVar(interp, part1, variable->getString().c_str()); } catch (MSXException& e) { static_string = e.getMessage(); return const_cast(static_string.c_str()); } } if (flags & TCL_TRACE_WRITES) { try { const char* v = getVar(interp, part1); string newValue = v ? v : ""; variable->setStringDirect(newValue); string newValue2 = variable->getString(); if (newValue != newValue2) { setVar(interp, part1, newValue2.c_str()); } } catch (MSXException& e) { setVar(interp, part1, getSafeValueString(*variable).c_str()); static_string = e.getMessage(); return const_cast(static_string.c_str()); } } if (flags & TCL_TRACE_UNSETS) { try { // note we cannot use restoreDefault(), because // that goes via Tcl and the Tcl variable // doesn't exist at this point variable->setStringDirect( variable->getRestoreValue()); } catch (MSXException&) { // for some reason default value is not valid ATM, // keep current value (happened for videosource // setting before turning on (set power on) the // MSX machine) } setVar(interp, part1, getSafeValueString(*variable).c_str()); Tcl_TraceVar(interp, part1, TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS, traceProc, reinterpret_cast(traceID)); } } catch (...) { UNREACHABLE; // we cannot let exceptions pass through Tcl } return nullptr; } void Interpreter::createNamespace(const std::string& name) { execute("namespace eval ::" + name + " {}"); } void Interpreter::deleteNamespace(const std::string& name) { execute("namespace delete ::" + name); } vector Interpreter::splitList(const std::string& list) { return splitList(list, interp); } vector Interpreter::splitList(const string& list, Tcl_Interp* interp) { int argc; const char** argv; if (Tcl_SplitList(interp, list.c_str(), &argc, &argv) == TCL_ERROR) { throw CommandException( interp ? Tcl_GetStringResult(interp) : "splitList error"); } vector result(argv, argv + argc); Tcl_Free(reinterpret_cast(argv)); return result; } int Interpreter::signalEvent(const std::shared_ptr& event) { (void)event; assert(event->getType() == OPENMSX_POLL_EVENT); poll(); return 0; } void Interpreter::poll() { //Tcl_ServiceAll(); Tcl_DoOneEvent(TCL_DONT_WAIT); } TclParser Interpreter::parse(string_ref command) { return TclParser(interp, command); } } // namespace openmsx openmsx-0.10.0/src/commands/ProxyCommand.hh0000644000175000017500000000115412262345041021376 0ustar manuelmanuel00000000000000#ifndef PROXYCOMMAND_HH #define PROXYCOMMAND_HH #include "Command.hh" #include "string_ref.hh" namespace openmsx { class CommandController; class Reactor; class ProxyCmd : public Command { public: ProxyCmd(CommandController& controller, Reactor& reactor); virtual void execute(const std::vector& tokens, TclObject& result); virtual std::string help(const std::vector& tokens) const; virtual void tabCompletion(std::vector& tokens) const; private: Command* getMachineCommand(string_ref name) const; Reactor& reactor; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/0000755000175000017500000000000012262345041015374 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/ide/MegaSCSI.hh0000644000175000017500000000203012262345041017243 0ustar manuelmanuel00000000000000#ifndef MEGASCSI_HH #define MEGASCSI_HH #include "MSXDevice.hh" #include namespace openmsx { class MB89352; class SRAM; class RomBlockDebuggable; class MegaSCSI : public MSXDevice { public: explicit MegaSCSI(const DeviceConfig& config); virtual ~MegaSCSI(); virtual void reset(EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual byte peekmem(word address, EmuTime::param time) const; virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte* getWriteCacheLine(word start) const; template void serialize(Archive& ar, unsigned version); private: void setSRAM(unsigned region, byte block); const std::unique_ptr mb89352; const std::unique_ptr sram; const std::unique_ptr romBlockDebug; bool isWriteable[4]; // which region is readonly? byte mapped[4]; // SPC block mapped in this region? const byte blockMask; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/SCSILS120.hh0000644000175000017500000000534212262345041017144 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDevice.h,v ** Revision: 1.6 ** Date: 2007-05-22 20:05:38 +0200 (Tue, 22 May 2007) ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, white cat */ #ifndef SCSILS120_HH #define SCSILS120_HH #include "HD.hh" #include "SCSIDevice.hh" #include "SectorAccessibleDisk.hh" #include "DiskContainer.hh" #include "serialize_meta.hh" #include "noncopyable.hh" #include namespace openmsx { class DeviceConfig; class MSXMotherBoard; class LSXCommand; class SCSILS120 : public SCSIDevice, public SectorAccessibleDisk, public DiskContainer, private noncopyable { public: SCSILS120(const DeviceConfig& targetconfig, AlignedBuffer& buf, unsigned mode); virtual ~SCSILS120(); template void serialize(Archive& ar, unsigned version); private: // SectorAccessibleDisk: virtual void readSectorImpl (size_t sector, SectorBuffer& buf); virtual void writeSectorImpl(size_t sector, const SectorBuffer& buf); virtual size_t getNbSectorsImpl() const; virtual bool isWriteProtectedImpl() const; virtual Sha1Sum getSha1Sum(); // Diskcontainer: virtual SectorAccessibleDisk* getSectorAccessibleDisk(); virtual const std::string& getContainerName() const; virtual bool diskChanged(); virtual int insertDisk(const std::string& filename); // SCSI Device virtual void reset(); virtual bool isSelected(); virtual unsigned executeCmd(const byte* cdb, SCSI::Phase& phase, unsigned& blocks); virtual unsigned executingCmd(SCSI::Phase& phase, unsigned& blocks); virtual byte getStatusCode(); virtual int msgOut(byte value); virtual byte msgIn(); virtual void disconnect(); virtual void busReset(); virtual void eject(); virtual void insert(const std::string& filename); virtual unsigned dataIn(unsigned& blocks); virtual unsigned dataOut(unsigned& blocks); bool getReady(); void testUnitReady(); void startStopUnit(); unsigned inquiry(); unsigned modeSense(); unsigned requestSense(); bool checkReadOnly(); unsigned readCapacity(); bool checkAddress(); unsigned readSector(unsigned& blocks); unsigned writeSector(unsigned& blocks); void formatUnit(); MSXMotherBoard& motherBoard; AlignedBuffer& buffer; std::unique_ptr file; std::unique_ptr lsxCommand; std::string name; const int mode; unsigned keycode; // Sense key, ASC, ASCQ unsigned currentSector; unsigned currentLength; const byte scsiId; // SCSI ID 0..7 bool unitAttention; // Unit Attention (was: reset) bool mediaChanged; // Enhanced change flag for MEGA-SCSI driver byte message; byte lun; byte cdb[12]; // Command Descriptor Block friend class LSXCommand; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/WD33C93.cc0000644000175000017500000003346212262345041016652 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/wd33c93.c,v ** Revision: 1.12 ** Date: 2007/03/25 17:05:07 ** ** Based on the WD33C93 emulation in MESS (www.mess.org). ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2006 Daniel Vik, Tomas Karlsson, white cat */ #include "WD33C93.hh" #include "SCSI.hh" #include "SCSIDevice.hh" #include "DummySCSIDevice.hh" #include "SCSIHD.hh" #include "SCSILS120.hh" #include "DeviceConfig.hh" #include "XMLElement.hh" #include "MSXException.hh" #include "StringOp.hh" #include "serialize.hh" #include "memory.hh" #include #include namespace openmsx { static const unsigned MAX_DEV = 8; static const byte REG_OWN_ID = 0x00; static const byte REG_CONTROL = 0x01; static const byte REG_TIMEO = 0x02; static const byte REG_TSECS = 0x03; static const byte REG_THEADS = 0x04; static const byte REG_TCYL_HI = 0x05; static const byte REG_TCYL_LO = 0x06; static const byte REG_ADDR_HI = 0x07; static const byte REG_ADDR_2 = 0x08; static const byte REG_ADDR_3 = 0x09; static const byte REG_ADDR_LO = 0x0a; static const byte REG_SECNO = 0x0b; static const byte REG_HEADNO = 0x0c; static const byte REG_CYLNO_HI = 0x0d; static const byte REG_CYLNO_LO = 0x0e; static const byte REG_TLUN = 0x0f; static const byte REG_CMD_PHASE = 0x10; static const byte REG_SYN = 0x11; static const byte REG_TCH = 0x12; static const byte REG_TCM = 0x13; static const byte REG_TCL = 0x14; static const byte REG_DST_ID = 0x15; static const byte REG_SRC_ID = 0x16; static const byte REG_SCSI_STATUS = 0x17; // (r) static const byte REG_CMD = 0x18; static const byte REG_DATA = 0x19; static const byte REG_QUEUE_TAG = 0x1a; static const byte REG_AUX_STATUS = 0x1f; // (r) static const byte REG_CDBSIZE = 0x00; static const byte REG_CDB1 = 0x03; static const byte REG_CDB2 = 0x04; static const byte REG_CDB3 = 0x05; static const byte REG_CDB4 = 0x06; static const byte REG_CDB5 = 0x07; static const byte REG_CDB6 = 0x08; static const byte REG_CDB7 = 0x09; static const byte REG_CDB8 = 0x0a; static const byte REG_CDB9 = 0x0b; static const byte REG_CDB10 = 0x0c; static const byte REG_CDB11 = 0x0d; static const byte REG_CDB12 = 0x0e; static const byte OWN_EAF = 0x08; // ENABLE ADVANCED FEATURES // SCSI STATUS static const byte SS_RESET = 0x00; // reset static const byte SS_RESET_ADV = 0x01; // reset w/adv. features static const byte SS_XFER_END = 0x16; // select and transfer complete static const byte SS_SEL_TIMEOUT = 0x42; // selection timeout static const byte SS_DISCONNECT = 0x85; // AUX STATUS static const byte AS_DBR = 0x01; // data buffer ready static const byte AS_CIP = 0x10; // command in progress, chip is busy static const byte AS_BSY = 0x20; // Level 2 command in progress static const byte AS_LCI = 0x40; // last command ignored static const byte AS_INT = 0x80; /* command phase 0x00 NO_SELECT 0x10 SELECTED 0x20 IDENTIFY_SENT 0x30 COMMAND_OUT 0x41 SAVE_DATA_RECEIVED 0x42 DISCONNECT_RECEIVED 0x43 LEGAL_DISCONNECT 0x44 RESELECTED 0x45 IDENTIFY_RECEIVED 0x46 DATA_TRANSFER_DONE 0x47 STATUS_STARTED 0x50 STATUS_RECEIVED 0x60 COMPLETE_RECEIVED */ WD33C93::WD33C93(const DeviceConfig& config) { devBusy = false; for (auto* t : config.getXML()->getChildren("target")) { unsigned id = t->getAttributeAsInt("id"); if (id >= MAX_DEV) { throw MSXException(StringOp::Builder() << "Invalid SCSI id: " << id << " (should be 0.." << MAX_DEV - 1 << ')'); } if (dev[id]) { throw MSXException(StringOp::Builder() << "Duplicate SCSI id: " << id); } DeviceConfig conf(config, *t); auto& type = t->getChild("type").getData(); if (type == "SCSIHD") { dev[id] = make_unique(conf, buffer, SCSIDevice::MODE_SCSI1 | SCSIDevice::MODE_UNITATTENTION | SCSIDevice::MODE_NOVAXIS); } else if (type == "SCSILS120") { dev[id] = make_unique(conf, buffer, SCSIDevice::MODE_SCSI1 | SCSIDevice::MODE_UNITATTENTION | SCSIDevice::MODE_NOVAXIS); } else { throw MSXException("Unknown SCSI device: " + type); } } // fill remaining targets with dummy SCSI devices to prevent crashes for (unsigned i = 0; i < MAX_DEV; ++i) { if (!dev[i]) { dev[i] = make_unique(); } } reset(false); // avoid UMR on savestate memset(buffer.data(), 0, SCSIDevice::BUFFER_SIZE); counter = 0; blockCounter = 0; targetId = 0; } WD33C93::~WD33C93() { } void WD33C93::disconnect() { if (phase != SCSI::BUS_FREE) { assert(targetId < MAX_DEV); dev[targetId]->disconnect(); if (regs[REG_SCSI_STATUS] != SS_XFER_END) { regs[REG_SCSI_STATUS] = SS_DISCONNECT; } regs[REG_AUX_STATUS] = AS_INT; phase = SCSI::BUS_FREE; } tc = 0; PRT_DEBUG("busfree()"); } void WD33C93::execCmd(byte value) { if (regs[REG_AUX_STATUS] & AS_CIP) { PRT_DEBUG("wd33c93ExecCmd() CIP error"); return; } //regs[REG_AUX_STATUS] |= AS_CIP; regs[REG_CMD] = value; bool atn = false; switch (value) { case 0x00: // Reset controller (software reset) PRT_DEBUG("wd33c93 [CMD] Reset controller"); memset(regs + 1, 0, 0x1a); disconnect(); latch = 0; // TODO: is this correct? Some doc says: reset to zero by masterreset-signal but not by reset command. regs[REG_SCSI_STATUS] = (regs[REG_OWN_ID] & OWN_EAF) ? SS_RESET_ADV : SS_RESET; break; case 0x02: // Assert ATN PRT_DEBUG("wd33c93 [CMD] Assert ATN"); break; case 0x04: // Disconnect PRT_DEBUG("wd33c93 [CMD] Disconnect"); disconnect(); break; case 0x06: // Select with ATN (Lv2) atn = true; // fall-through case 0x07: // Select Without ATN (Lv2) PRT_DEBUG("wd33c93 [CMD] Select (ATN " << atn << ')'); targetId = regs[REG_DST_ID] & 7; regs[REG_SCSI_STATUS] = SS_SEL_TIMEOUT; tc = 0; regs[REG_AUX_STATUS] = AS_INT; break; case 0x08: // Select with ATN and transfer (Lv2) atn = true; // fall-through case 0x09: // Select without ATN and Transfer (Lv2) PRT_DEBUG("wd33c93 [CMD] Select and transfer (ATN " << atn << ')'); targetId = regs[REG_DST_ID] & 7; if (!devBusy && targetId < MAX_DEV && /* targetId != myId && */ dev[targetId]->isSelected()) { if (atn) { dev[targetId]->msgOut(regs[REG_TLUN] | 0x80); } devBusy = true; counter = dev[targetId]->executeCmd( ®s[REG_CDB1], phase, blockCounter); switch (phase) { case SCSI::STATUS: devBusy = false; regs[REG_TLUN] = dev[targetId]->getStatusCode(); dev[targetId]->msgIn(); regs[REG_SCSI_STATUS] = SS_XFER_END; disconnect(); break; case SCSI::EXECUTE: regs[REG_AUX_STATUS] = AS_CIP | AS_BSY; bufIdx = 0; break; default: devBusy = false; regs[REG_AUX_STATUS] = AS_CIP | AS_BSY | AS_DBR; bufIdx = 0; } //regs[REG_SRC_ID] |= regs[REG_DST_ID] & 7; } else { PRT_DEBUG("wd33c93 timeout on target " << int(targetId)); tc = 0; regs[REG_SCSI_STATUS] = SS_SEL_TIMEOUT; regs[REG_AUX_STATUS] = AS_INT; } break; case 0x18: // Translate Address (Lv2) default: PRT_DEBUG("wd33c93 [CMD] unsupport command " << int(value)); break; } } void WD33C93::writeAdr(byte value) { //PRT_DEBUG("WriteAdr value " << std::hex << (int)value); latch = value & 0x1f; } // Latch incremented by one each time a register is accessed, // except for the address-, aux.status-, data- and command registers. void WD33C93::writeCtrl(byte value) { //PRT_DEBUG("wd33c93 write #" << std::hex << (int)latch << ", " << (int)value); switch (latch) { case REG_OWN_ID: regs[REG_OWN_ID] = value; myId = value & 7; PRT_DEBUG("wd33c93 myid = " << int(myId)); break; case REG_TCH: tc = (tc & 0x00ffff) + (value << 16); break; case REG_TCM: tc = (tc & 0xff00ff) + (value << 8); break; case REG_TCL: tc = (tc & 0xffff00) + (value << 0); break; case REG_CMD_PHASE: PRT_DEBUG("wd33c93 CMD_PHASE = " << int(value)); regs[REG_CMD_PHASE] = value; break; case REG_CMD: execCmd(value); return; // no latch-inc for address-, aux.status-, data- and command regs. case REG_DATA: regs[REG_DATA] = value; if (phase == SCSI::DATA_OUT) { buffer[bufIdx++] = value; --tc; if (--counter == 0) { counter = dev[targetId]->dataOut(blockCounter); if (counter) { bufIdx = 0; return; } regs[REG_TLUN] = dev[targetId]->getStatusCode(); dev[targetId]->msgIn(); regs[REG_SCSI_STATUS] = SS_XFER_END; disconnect(); } } return; // no latch-inc for address-, aux.status-, data- and command regs. case REG_AUX_STATUS: return; // no latch-inc for address-, aux.status-, data- and command regs. default: if (latch <= REG_SRC_ID) { regs[latch] = value; } break; } latch = (latch + 1) & 0x1f; } byte WD33C93::readAuxStatus() { byte rv = regs[REG_AUX_STATUS]; if (phase == SCSI::EXECUTE) { counter = dev[targetId]->executingCmd(phase, blockCounter); switch (phase) { case SCSI::STATUS: // TODO how can this ever be the case? regs[REG_TLUN] = dev[targetId]->getStatusCode(); dev[targetId]->msgIn(); regs[REG_SCSI_STATUS] = SS_XFER_END; disconnect(); break; case SCSI::EXECUTE: break; default: regs[REG_AUX_STATUS] |= AS_DBR; } } //PRT_DEBUG("readAuxStatus returning " << std::hex << (int)rv); return rv; } // Latch incremented by one each time a register is accessed, // except for the address-, aux.status-, data- and command registers. byte WD33C93::readCtrl() { //PRT_DEBUG("ReadCtrl"); byte rv; switch (latch) { case REG_SCSI_STATUS: rv = regs[REG_SCSI_STATUS]; //PRT_DEBUG1("wd33c93 SCSI_STATUS = %X\n", rv); if (rv != SS_XFER_END) { regs[REG_AUX_STATUS] &= ~AS_INT; } else { regs[REG_SCSI_STATUS] = SS_DISCONNECT; regs[REG_AUX_STATUS] = AS_INT; } break; case REG_CMD: return regs[REG_CMD]; // no latch-inc for address-, aux.status-, data- and command regs. case REG_DATA: if (phase == SCSI::DATA_IN) { rv = buffer[bufIdx++]; regs[REG_DATA] = rv; --tc; if (--counter == 0) { if (blockCounter > 0) { counter = dev[targetId]->dataIn(blockCounter); if (counter) { bufIdx = 0; return rv; } } regs[REG_TLUN] = dev[targetId]->getStatusCode(); dev[targetId]->msgIn(); regs[REG_SCSI_STATUS] = SS_XFER_END; disconnect(); } } else { rv = regs[REG_DATA]; } return rv; // no latch-inc for address-, aux.status-, data- and command regs. case REG_TCH: rv = (tc >> 16) & 0xff; break; case REG_TCM: rv = (tc >> 8) & 0xff; break; case REG_TCL: rv = (tc >> 0) & 0xff; break; case REG_AUX_STATUS: return readAuxStatus(); // no latch-inc for address-, aux.status-, data- and command regs. default: rv = regs[latch]; break; } //PRT_DEBUG2("wd33c93 read #%X, %X\n", latch, rv); latch = (latch + 1) & 0x1f; return rv; } byte WD33C93::peekAuxStatus() const { return regs[REG_AUX_STATUS]; } byte WD33C93::peekCtrl() const { switch (latch) { case REG_TCH: return (tc >> 16) & 0xff; case REG_TCM: return (tc >> 8) & 0xff; case REG_TCL: return (tc >> 0) & 0xff; default: return regs[latch]; } } void WD33C93::reset(bool scsireset) { PRT_DEBUG("wd33c93 reset"); // initialized register memset(regs, 0, 0x1b); memset(regs + 0x1b, 0xff, 4); regs[REG_AUX_STATUS] = AS_INT; myId = 0; latch = 0; tc = 0; phase = SCSI::BUS_FREE; bufIdx = 0; if (scsireset) { for (unsigned i = 0; i < MAX_DEV; ++i) { dev[i]->reset(); } } } static enum_string phaseInfo[] = { { "UNDEFINED", SCSI::UNDEFINED }, { "BUS_FREE", SCSI::BUS_FREE }, { "ARBITRATION", SCSI::ARBITRATION }, { "SELECTION", SCSI::SELECTION }, { "RESELECTION", SCSI::RESELECTION }, { "COMMAND", SCSI::COMMAND }, { "EXECUTE", SCSI::EXECUTE }, { "DATA_IN", SCSI::DATA_IN }, { "DATA_OUT", SCSI::DATA_OUT }, { "STATUS", SCSI::STATUS }, { "MSG_OUT", SCSI::MSG_OUT }, { "MSG_IN", SCSI::MSG_IN } }; SERIALIZE_ENUM(SCSI::Phase, phaseInfo); template void WD33C93::serialize(Archive& ar, unsigned /*version*/) { ar.serialize_blob("buffer", buffer.data(), buffer.size()); char tag[8] = { 'd', 'e', 'v', 'i', 'c', 'e', 'X', 0 }; for (unsigned i = 0; i < MAX_DEV; ++i) { tag[6] = char('0' + i); ar.serializePolymorphic(tag, *dev[i]); } ar.serialize("bufIdx", bufIdx); ar.serialize("counter", counter); ar.serialize("blockCounter", blockCounter); ar.serialize("tc", tc); ar.serialize("phase", phase); ar.serialize("myId", myId); ar.serialize("targetId", targetId); ar.serialize_blob("registers", regs, sizeof(regs)); ar.serialize("latch", latch); ar.serialize("devBusy", devBusy); } INSTANTIATE_SERIALIZE_METHODS(WD33C93); /* Here is some info on the parameters for SCSI devices: static SCSIDevice* wd33c93ScsiDevCreate(WD33C93* wd33c93, int id) { #if 1 // CD_UPDATE: Use dynamic parameters instead of hard coded ones int diskId, mode, type; diskId = diskGetHdDriveId(hdId, id); if (diskIsCdrom(diskId)) { mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_REMOVABLE | MODE_NOVAXIS; type = SDT_CDROM; } else { mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_FDS120 | MODE_REMOVABLE | MODE_NOVAXIS; type = SDT_DirectAccess; } return scsiDeviceCreate(id, diskId, buffer, nullptr, type, mode, (CdromXferCompCb)wd33c93XferCb, wd33c93); #else SCSIDEVICE* dev; int mode; int type; if (id != 2) { mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_FDS120 | MODE_REMOVABLE | MODE_NOVAXIS; type = SDT_DirectAccess; } else { mode = MODE_SCSI1 | MODE_UNITATTENTION | MODE_REMOVABLE | MODE_NOVAXIS; type = SDT_CDROM; } dev = scsiDeviceCreate(id, diskGetHdDriveId(hdId, id), buffer, nullptr, type, mode, (CdromXferCompCb)wd33c93XferCb, wd33c93); return dev; #endif } */ } // namespace openmsx openmsx-0.10.0/src/ide/SCSIHD.hh0000644000175000017500000000342612262345041016677 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDevice.h,v ** Revision: 1.6 ** Date: 2007-05-22 20:05:38 +0200 (Tue, 22 May 2007) ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, white cat */ #ifndef SCSIHD_HH #define SCSIHD_HH #include "HD.hh" #include "SCSIDevice.hh" #include "serialize_meta.hh" #include "noncopyable.hh" #include namespace openmsx { class DeviceConfig; class MSXMotherBoard; class SCSIHD : public HD, public SCSIDevice, private noncopyable { public: SCSIHD(const DeviceConfig& targetconfig, AlignedBuffer& buf, unsigned mode); virtual ~SCSIHD(); template void serialize(Archive& ar, unsigned version); private: // SCSI Device virtual void reset(); virtual bool isSelected(); virtual unsigned executeCmd(const byte* cdb, SCSI::Phase& phase, unsigned& blocks); virtual unsigned executingCmd(SCSI::Phase& phase, unsigned& blocks); virtual byte getStatusCode(); virtual int msgOut(byte value); virtual byte msgIn(); virtual void disconnect(); virtual void busReset(); virtual unsigned dataIn(unsigned& blocks); virtual unsigned dataOut(unsigned& blocks); unsigned inquiry(); unsigned modeSense(); unsigned requestSense(); bool checkReadOnly(); unsigned readCapacity(); bool checkAddress(); unsigned readSectors(unsigned& blocks); unsigned writeSectors(unsigned& blocks); void formatUnit(); AlignedBuffer& buffer; const unsigned mode; unsigned keycode; // Sense key, ASC, ASCQ unsigned currentSector; unsigned currentLength; const byte scsiId; // SCSI ID 0..7 bool unitAttention; // Unit Attention (was: reset) byte message; byte lun; byte cdb[12]; // Command Descriptor Block }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/SunriseIDE.cc0000644000175000017500000001256312262345041017664 0ustar manuelmanuel00000000000000#include "SunriseIDE.hh" #include "RomBlockDebuggable.hh" #include "IDEDeviceFactory.hh" #include "IDEDevice.hh" #include "Rom.hh" #include "Math.hh" #include "serialize.hh" #include "memory.hh" namespace openmsx { SunriseIDE::SunriseIDE(const DeviceConfig& config) : MSXDevice(config) , rom(make_unique(getName() + " ROM", "rom", config)) , romBlockDebug(make_unique( *this, &control, 0x4000, 0x4000, 14)) { device[0] = IDEDeviceFactory::create( DeviceConfig(config, config.findChild("master"))); device[1] = IDEDeviceFactory::create( DeviceConfig(config, config.findChild("slave"))); // make valgrind happy internalBank = nullptr; ideRegsEnabled = false; powerUp(getCurrentTime()); } SunriseIDE::~SunriseIDE() { } void SunriseIDE::powerUp(EmuTime::param time) { writeControl(0xFF); reset(time); } void SunriseIDE::reset(EmuTime::param time) { selectedDevice = 0; softReset = false; device[0]->reset(time); device[1]->reset(time); } byte SunriseIDE::readMem(word address, EmuTime::param time) { if (ideRegsEnabled && ((address & 0x3E00) == 0x3C00)) { // 0x7C00 - 0x7DFF ide data register if ((address & 1) == 0) { return readDataLow(time); } else { return readDataHigh(time); } } if (ideRegsEnabled && ((address & 0x3F00) == 0x3E00)) { // 0x7E00 - 0x7EFF ide registers return readReg(address & 0xF, time); } if ((0x4000 <= address) && (address < 0x8000)) { // read normal (flash) rom return internalBank[address & 0x3FFF]; } // read nothing return 0xFF; } const byte* SunriseIDE::getReadCacheLine(word start) const { if (ideRegsEnabled && ((start & 0x3E00) == 0x3C00)) { return nullptr; } if (ideRegsEnabled && ((start & 0x3F00) == 0x3E00)) { return nullptr; } if ((0x4000 <= start) && (start < 0x8000)) { return &internalBank[start & 0x3FFF]; } return unmappedRead; } void SunriseIDE::writeMem(word address, byte value, EmuTime::param time) { if ((address & 0xBF04) == 0x0104) { // control register writeControl(value); return; } if (ideRegsEnabled && ((address & 0x3E00) == 0x3C00)) { // 0x7C00 - 0x7DFF ide data register if ((address & 1) == 0) { writeDataLow(value); } else { writeDataHigh(value, time); } return; } if (ideRegsEnabled && ((address & 0x3F00) == 0x3E00)) { // 0x7E00 - 0x7EFF ide registers writeReg(address & 0xF, value, time); return; } // all other writes ignored } void SunriseIDE::writeControl(byte value) { control = value; if (ideRegsEnabled != (control & 1)) { ideRegsEnabled = control & 1; invalidateMemCache(0x3C00, 0x0300); invalidateMemCache(0x7C00, 0x0300); invalidateMemCache(0xBC00, 0x0300); invalidateMemCache(0xFC00, 0x0300); } byte bank = Math::reverseByte(control & 0xF8); if (bank >= (rom->getSize() / 0x4000)) { bank &= ((rom->getSize() / 0x4000) - 1); } if (internalBank != &(*rom)[0x4000 * bank]) { internalBank = &(*rom)[0x4000 * bank]; invalidateMemCache(0x4000, 0x4000); } } byte SunriseIDE::readDataLow(EmuTime::param time) { word temp = readData(time); readLatch = temp >> 8; return temp & 0xFF; } byte SunriseIDE::readDataHigh(EmuTime::param /*time*/) { return readLatch; } word SunriseIDE::readData(EmuTime::param time) { word result = device[selectedDevice]->readData(time); //PRT_DEBUG("IDE read data: 0x" << std::hex << int(result) << std::dec); return result; } byte SunriseIDE::readReg(nibble reg, EmuTime::param time) { //PRT_DEBUG("IDE read reg: " << (int)reg); byte result; if (reg == 14) { // alternate status register reg = 7; } if (softReset) { if (reg == 7) { // read status result = 0xFF; // BUSY } else { // all others result = 0x7F; // don't care } } else { if (reg == 0) { result = readData(time) & 0xFF; } else { result = device[selectedDevice]->readReg(reg, time); if (reg == 6) { result &= 0xEF; result |= selectedDevice ? 0x10 : 0x00; } } } return result; } void SunriseIDE::writeDataLow(byte value) { writeLatch = value; } void SunriseIDE::writeDataHigh(byte value, EmuTime::param time) { word temp = (value << 8) | writeLatch; writeData(temp, time); } void SunriseIDE::writeData(word value, EmuTime::param time) { device[selectedDevice]->writeData(value, time); } void SunriseIDE::writeReg(nibble reg, byte value, EmuTime::param time) { if (softReset) { if ((reg == 14) && !(value & 0x04)) { // clear SRST softReset = false; } // ignore all other writes } else { if (reg == 0) { writeData((value << 8) | value, time); } else { if ((reg == 14) && (value & 0x04)) { // set SRST softReset = true; device[0]->reset(time); device[1]->reset(time); } else { if (reg == 6) { selectedDevice = (value & 0x10) ? 1 : 0; } device[selectedDevice]->writeReg(reg, value, time); } } } } template void SunriseIDE::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serializePolymorphic("master", *device[0]); ar.serializePolymorphic("slave", *device[1]); ar.serialize("readLatch", readLatch); ar.serialize("writeLatch", writeLatch); ar.serialize("selectedDevice", selectedDevice); ar.serialize("control", control); ar.serialize("softReset", softReset); if (ar.isLoader()) { // restore internalBank, ideRegsEnabled writeControl(control); } } INSTANTIATE_SERIALIZE_METHODS(SunriseIDE); REGISTER_MSXDEVICE(SunriseIDE, "SunriseIDE"); } // namespace openmsx openmsx-0.10.0/src/ide/IDECDROM.hh0000644000175000017500000000336412262345041017111 0ustar manuelmanuel00000000000000#ifndef IDECDROM_HH #define IDECDROM_HH #include "AbstractIDEDevice.hh" #include "serialize_meta.hh" #include "noncopyable.hh" #include namespace openmsx { class MSXMotherBoard; class DeviceConfig; class File; class CDXCommand; class IDECDROM : public AbstractIDEDevice, private noncopyable { public: explicit IDECDROM(const DeviceConfig& config); virtual ~IDECDROM(); void eject(); void insert(const std::string& filename); template void serialize(Archive& ar, unsigned version); protected: // AbstractIDEDevice: virtual bool isPacketDevice(); virtual const std::string& getDeviceName(); virtual void fillIdentifyBlock (AlignedBuffer& buffer); virtual unsigned readBlockStart(AlignedBuffer& buffer, unsigned count); virtual void readEnd(); virtual void writeBlockComplete(AlignedBuffer& buffer, unsigned count); virtual void executeCommand(byte cmd); private: // Flags for the interrupt reason register: /** Bus release: 0 = normal, 1 = bus release */ static const byte REL = 0x04; /** I/O direction: 0 = host->device, 1 = device->host */ static const byte I_O = 0x02; /** Command/data: 0 = data, 1 = command */ static const byte C_D = 0x01; /** Indicates the start of a read data transfer performed in packets. * @param count Total number of bytes to transfer. */ void startPacketReadTransfer(unsigned count); void executePacketCommand(AlignedBuffer& packet); std::string name; std::unique_ptr cdxCommand; std::unique_ptr file; unsigned byteCountLimit; unsigned transferOffset; unsigned senseKey; bool readSectorData; // Removable Media Status Notification Feature Set bool remMedStatNotifEnabled; bool mediaChanged; friend class CDXCommand; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/AbstractIDEDevice.cc0000644000175000017500000002546312262345041021122 0ustar manuelmanuel00000000000000#include "AbstractIDEDevice.hh" #include "MSXMotherBoard.hh" #include "LedStatus.hh" #include "Version.hh" #include "serialize.hh" #include "unreachable.hh" #include #include #include namespace openmsx { AbstractIDEDevice::AbstractIDEDevice(MSXMotherBoard& motherBoard_) : motherBoard(motherBoard_) { transferRead = false; transferWrite = false; transferIdx = 0; // avoid UMR on serialize memset(buffer, 0, sizeof(buffer)); bufferLeft = 0; transferCount = 0; } AbstractIDEDevice::~AbstractIDEDevice() { } byte AbstractIDEDevice::diagnostic() { // The Execute Device Diagnostic command is executed by both devices in // parallel. Fortunately, returning 0x01 is valid in all cases: // - for device 0 it means: device 0 passed, device 1 passed or not present // - for device 1 it means: device 1 passed return 0x01; } void AbstractIDEDevice::createSignature(bool preserveDevice) { sectorCountReg = 0x01; sectorNumReg = 0x01; if (isPacketDevice()) { cylinderLowReg = 0x14; cylinderHighReg = 0xEB; if (preserveDevice) { devHeadReg &= 0x10; } else { // The current implementation of SunriseIDE will substitute the // current device for DEV, so it will always act like DEV is // preserved. devHeadReg = 0x00; } } else { cylinderLowReg = 0x00; cylinderHighReg = 0x00; devHeadReg = 0x00; } } void AbstractIDEDevice::reset(EmuTime::param /*time*/) { errorReg = diagnostic(); statusReg = DRDY | DSC; featureReg = 0x00; createSignature(); setTransferRead(false); setTransferWrite(false); } byte AbstractIDEDevice::readReg(nibble reg, EmuTime::param /*time*/) { switch (reg) { case 1: // error register return errorReg; case 2: // sector count register return sectorCountReg; case 3: // sector number register / LBA low return sectorNumReg; case 4: // cyclinder low register / LBA mid return cylinderLowReg; case 5: // cyclinder high register / LBA high return cylinderHighReg; case 6: // device/head register // DEV bit is handled by IDE interface return devHeadReg; case 7: // status register return statusReg; case 8: case 9: case 10: case 11: case 12: case 13: case 15:// not used return 0x7F; case 0: // data register, converted to readData by IDE interface case 14:// alternate status reg, converted to read from normal // status register by IDE interface default: UNREACHABLE; return 0x7F; // avoid warning } } void AbstractIDEDevice::writeReg( nibble reg, byte value, EmuTime::param /*time*/ ) { switch (reg) { case 1: // feature register featureReg = value; break; case 2: // sector count register sectorCountReg = value; break; case 3: // sector number register / LBA low sectorNumReg = value; break; case 4: // cyclinder low register / LBA mid cylinderLowReg = value; break; case 5: // cyclinder high register / LBA high cylinderHighReg = value; break; case 6: // device/head register // DEV bit is handled by IDE interface devHeadReg = value; break; case 7: // command register statusReg &= ~(DRQ | ERR); setTransferRead(false); setTransferWrite(false); executeCommand(value); break; case 8: case 9: case 10: case 11: case 12: case 13: case 15: // not used case 14: // device control register, handled by IDE interface // do nothing break; case 0: // data register, converted to readData by IDE interface default: UNREACHABLE; break; } } word AbstractIDEDevice::readData(EmuTime::param /*time*/) { if (!transferRead) { // no read in progress return 0x7F7F; } assert((transferIdx + 1) < sizeof(buffer)); word result = (buffer[transferIdx + 0] << 0) + (buffer[transferIdx + 1] << 8); transferIdx += 2; bufferLeft -= 2; if (bufferLeft == 0) { if (transferCount == 0) { // End of transfer. setTransferRead(false); statusReg &= ~DRQ; readEnd(); } else { // Buffer empty, but transfer not done yet. readNextBlock(); } } return result; } void AbstractIDEDevice::readNextBlock() { bufferLeft = readBlockStart( buffer, std::min(sizeof(buffer), transferCount)); assert((bufferLeft & 1) == 0); transferIdx = 0; transferCount -= bufferLeft; } void AbstractIDEDevice::writeData(word value, EmuTime::param /*time*/) { if (!transferWrite) { // no write in progress return; } assert((transferIdx + 1) < sizeof(buffer)); buffer[transferIdx + 0] = value & 0xFF; buffer[transferIdx + 1] = value >> 8; transferIdx += 2; bufferLeft -= 2; if (bufferLeft == 0) { unsigned bytesInBuffer = transferIdx; if (transferCount == 0) { // End of transfer. setTransferWrite(false); statusReg &= ~DRQ; } else { // Buffer full, but transfer not done yet. writeNextBlock(); } // Packet commands can start a second transfer, so the command // execution must happen after we close this transfer. writeBlockComplete(buffer, bytesInBuffer); } } void AbstractIDEDevice::writeNextBlock() { transferIdx = 0; bufferLeft = std::min(sizeof(buffer), transferCount); transferCount -= bufferLeft; } void AbstractIDEDevice::setError(byte error) { errorReg = error; if (error) { statusReg |= ERR; } else { statusReg &= ~ERR; } statusReg &= ~DRQ; setTransferWrite(false); setTransferRead(false); } unsigned AbstractIDEDevice::getSectorNumber() const { return sectorNumReg | (cylinderLowReg << 8) | (cylinderHighReg << 16) | ((devHeadReg & 0x0F) << 24); } unsigned AbstractIDEDevice::getNumSectors() const { return (sectorCountReg == 0) ? 256 : sectorCountReg; } void AbstractIDEDevice::setInterruptReason(byte value) { sectorCountReg = value; } unsigned AbstractIDEDevice::getByteCount() { return cylinderLowReg | (cylinderHighReg << 8); } void AbstractIDEDevice::setByteCount(unsigned count) { cylinderLowReg = count & 0xFF; cylinderHighReg = count >> 8; } void AbstractIDEDevice::setSectorNumber(unsigned lba) { sectorNumReg = (lba & 0x000000FF) >> 0; cylinderLowReg = (lba & 0x0000FF00) >> 8; cylinderHighReg = (lba & 0x00FF0000) >> 16; devHeadReg = (lba & 0x0F000000) >> 24; // note: only 4 bits } void AbstractIDEDevice::readEnd() { } void AbstractIDEDevice::executeCommand(byte cmd) { switch (cmd) { case 0x08: // Device Reset if (isPacketDevice()) { errorReg = diagnostic(); createSignature(true); // TODO: Which is correct? //statusReg = 0x00; statusReg = DRDY | DSC; } else { // Command is only implemented for packet devices. setError(ABORT); } break; case 0x90: // Execute Device Diagnostic errorReg = diagnostic(); createSignature(); break; case 0x91: // Initialize Device Parameters // ignore command break; case 0xA1: // Identify Packet Device if (isPacketDevice()) { createIdentifyBlock(startShortReadTransfer(512)); } else { setError(ABORT); } break; case 0xEC: // Identify Device if (isPacketDevice()) { setError(ABORT); } else { createIdentifyBlock(startShortReadTransfer(512)); } break; case 0xEF: // Set Features switch (getFeatureReg()) { case 0x03: // Set Transfer Mode break; default: fprintf(stderr, "Unhandled set feature subcommand: %02X\n", getFeatureReg()); setError(ABORT); } break; default: // unsupported command fprintf(stderr, "unsupported IDE command %02X\n", cmd); setError(ABORT); } } AlignedBuffer& AbstractIDEDevice::startShortReadTransfer(unsigned count) { assert(count <= sizeof(buffer)); assert((count & 1) == 0); startReadTransfer(); transferCount = 0; bufferLeft = count; transferIdx = 0; memset(buffer, 0x00, count); return buffer; } void AbstractIDEDevice::startLongReadTransfer(unsigned count) { assert((count & 1) == 0); startReadTransfer(); transferCount = count; readNextBlock(); } void AbstractIDEDevice::startReadTransfer() { statusReg |= DRQ; setTransferRead(true); } void AbstractIDEDevice::abortReadTransfer(byte error) { setError(error | ABORT); setTransferRead(false); } void AbstractIDEDevice::startWriteTransfer(unsigned count) { statusReg |= DRQ; setTransferWrite(true); transferCount = count; writeNextBlock(); } void AbstractIDEDevice::abortWriteTransfer(byte error) { setError(error | ABORT); setTransferWrite(false); } void AbstractIDEDevice::setTransferRead(bool status) { if (status != transferRead) { transferRead = status; if (!transferWrite) { // (this is a bit of a hack!) motherBoard.getLedStatus().setLed(LedStatus::FDD, transferRead); } } } void AbstractIDEDevice::setTransferWrite(bool status) { if (status != transferWrite) { transferWrite = status; if (!transferRead) { // (this is a bit of a hack!) motherBoard.getLedStatus().setLed(LedStatus::FDD, transferWrite); } } } /** Writes a string to a location in the identify block. * Helper method for createIdentifyBlock. * @param p Pointer to write the characters to. * @param len Number of words to write. * @param s ASCII string to write. * If the string is longer than len*2 characters, it is truncated. * If the string is shorter than len*2 characters, it is padded with spaces. */ static void writeIdentifyString(byte* p, unsigned len, std::string s) { s.resize(2 * len, ' '); for (unsigned i = 0; i < len; ++i) { // copy and swap p[2 * i + 0] = s[2 * i + 1]; p[2 * i + 1] = s[2 * i + 0]; } } void AbstractIDEDevice::createIdentifyBlock(AlignedBuffer& buffer) { // According to the spec, the combination of model and serial should be // unique. But I don't know any MSX software that cares about this. writeIdentifyString(&buffer[10 * 2], 10, "s00000001"); // serial writeIdentifyString(&buffer[23 * 2], 4, // Use openMSX version as firmware revision, because most of our // IDE emulation code is in fact emulating the firmware. Version::RELEASE ? 'v' + std::string(Version::VERSION) : 'd' + std::string(Version::REVISION)); writeIdentifyString(&buffer[27 * 2], 20, getDeviceName()); // model fillIdentifyBlock(buffer); } template void AbstractIDEDevice::serialize(Archive& ar, unsigned /*version*/) { // no need to serialize IDEDevice base class ar.serialize_blob("buffer", buffer, sizeof(buffer)); ar.serialize("transferIdx", transferIdx); ar.serialize("bufferLeft", bufferLeft); ar.serialize("transferCount", transferCount); ar.serialize("errorReg", errorReg); ar.serialize("sectorCountReg", sectorCountReg); ar.serialize("sectorNumReg", sectorNumReg); ar.serialize("cylinderLowReg", cylinderLowReg); ar.serialize("cylinderHighReg", cylinderHighReg); ar.serialize("devHeadReg", devHeadReg); ar.serialize("statusReg", statusReg); ar.serialize("featureReg", featureReg); bool transferIdentifyBlock = false; // remove on next version increment // no need to break bw-compat now ar.serialize("transferIdentifyBlock", transferIdentifyBlock); ar.serialize("transferRead", transferRead); ar.serialize("transferWrite", transferWrite); } INSTANTIATE_SERIALIZE_METHODS(AbstractIDEDevice); } // namespace openmsx openmsx-0.10.0/src/ide/node.mk0000644000175000017500000000047412262345041016657 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ HD HDCommand \ SunriseIDE \ DummyIDEDevice \ IDEDeviceFactory \ AbstractIDEDevice IDEHD IDECDROM \ HDImageCLI CDImageCLI \ DummySCSIDevice SCSIHD SCSILS120 \ WD33C93 GoudaSCSI \ MB89352 MegaSCSI HDR_ONLY:= \ IDEDevice \ SCSI SCSIDevice include build/node-end.mk openmsx-0.10.0/src/ide/SCSIHD.cc0000644000175000017500000003624012262345041016665 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDevice.c,v ** Revision: 1.10 ** Date: 2007-05-21 21:38:29 +0200 (Mon, 21 May 2007) ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, white cat */ /* * Notes: * It follows the SCSI1(CCS) standard or the SCSI2 standard. * Only the direct access device is supported now. * Message system might be imperfect. * * NOTE: this version only supports a non-removable harddisk, as the class * name suggests. Refer to revision 6526 of this file to see what was removed * from the generic/parameterised code. */ #include "SCSIHD.hh" #include "FileOperations.hh" #include "MSXException.hh" #include "LedStatus.hh" #include "MSXMotherBoard.hh" #include "DeviceConfig.hh" #include "endian.hh" #include "serialize.hh" #include #include using std::string; namespace openmsx { // Medium type (value like LS-120) static const byte MT_UNKNOWN = 0x00; static const byte MT_2DD_UN = 0x10; static const byte MT_2DD = 0x11; static const byte MT_2HD_UN = 0x20; static const byte MT_2HD_12_98 = 0x22; static const byte MT_2HD_12 = 0x23; static const byte MT_2HD_144 = 0x24; static const byte MT_LS120 = 0x31; static const byte MT_NO_DISK = 0x70; static const byte MT_DOOR_OPEN = 0x71; static const byte MT_FMT_ERROR = 0x72; static const byte inqdata[36] = { 0, // bit5-0 device type code. 0, // bit7 = 1 removable device 2, // bit7,6 ISO version. bit5,4,3 ECMA version. // bit2,1,0 ANSI Version (001=SCSI1, 010=SCSI2) 2, // bit7 AENC. bit6 TrmIOP. // bit3-0 Response Data Format. (0000=SCSI1, 0001=CCS, 0010=SCSI2) 51, // addtional length 0, 0,// reserved 0, // bit7 RelAdr, bit6 WBus32, bit5 Wbus16, bit4 Sync, bit3 Linked, // bit2 reseved bit1 CmdQue, bit0 SftRe 'o', 'p', 'e', 'n', 'M', 'S', 'X', ' ', // vendor ID (8bytes) 'S', 'C', 'S', 'I', '2', ' ', 'H', 'a', // product ID (16bytes) 'r', 'd', 'd', 'i', 's', 'k', ' ', ' ', '0', '1', '0', 'a' // product version (ASCII 4bytes) }; static const unsigned BUFFER_BLOCK_SIZE = SCSIHD::BUFFER_SIZE / SectorAccessibleDisk::SECTOR_SIZE; SCSIHD::SCSIHD(const DeviceConfig& targetconfig, AlignedBuffer& buf, unsigned mode_) : HD(targetconfig) , buffer(buf) , mode(mode_) , scsiId(targetconfig.getAttributeAsInt("id")) { reset(); } SCSIHD::~SCSIHD() { PRT_DEBUG("hdd close for hdd " << int(scsiId)); } void SCSIHD::reset() { currentSector = 0; currentLength = 0; busReset(); } void SCSIHD::busReset() { PRT_DEBUG("SCSI: bus reset on " << int(scsiId)); keycode = 0; unitAttention = (mode & MODE_UNITATTENTION) != 0; } void SCSIHD::disconnect() { getMotherBoard().getLedStatus().setLed(LedStatus::FDD, false); } // Check the initiator in the call origin. bool SCSIHD::isSelected() { lun = 0; return true; } unsigned SCSIHD::inquiry() { unsigned length = currentLength; if (length == 0) return 0; memcpy(buffer + 2, inqdata + 2, 34); buffer[0] = SCSI::DT_DirectAccess; buffer[1] = 0; // removable if (!(mode & BIT_SCSI2)) { buffer[2] = 1; buffer[3] = 1; buffer[20] = '1'; } else { if (mode & BIT_SCSI3) { buffer[2] = 5; buffer[20] = '3'; } } if (mode & BIT_SCSI3) { length = std::min(length, 96u); buffer[4] = 91; if (length > 56) { memset(buffer + 56, 0, 40); buffer[58] = 0x03; buffer[60] = 0x01; buffer[61] = 0x80; } } else { length = std::min(length, 56u); } if (length > 36) { string filename = FileOperations::getFilename( getImageName().getOriginal()).str(); filename.resize(20, ' '); memcpy(buffer + 36, filename.data(), 20); } return length; } unsigned SCSIHD::modeSense() { byte* pBuffer = buffer; if ((currentLength > 0) && (cdb[2] == 3)) { // TODO check for too many sectors unsigned total = unsigned(getNbSectors()); byte media = MT_UNKNOWN; byte sectors = 64; byte blockLength = SECTOR_SIZE >> 8; byte tracks = 8; byte size = 4 + 24; byte removable = 0x80; // == not removable memset(pBuffer + 2, 0, 34); if (total == 0) { media = MT_NO_DISK; } // Mode Parameter Header 4bytes pBuffer[1] = media; // Medium Type pBuffer[3] = 8; // block descripter length pBuffer += 4; // Disable Block Descriptor check if (!(cdb[1] & 0x08)) { // Block Descriptor 8bytes pBuffer[1] = (total >> 16) & 0xff; // 1..3 Number of Blocks pBuffer[2] = (total >> 8) & 0xff; pBuffer[3] = (total >> 0) & 0xff; pBuffer[6] = blockLength & 0xff; // 5..7 Block Length in Bytes pBuffer += 8; size += 8; } // Format Device Page 24bytes pBuffer[ 0] = 3; // 0 Page pBuffer[ 1] = 0x16; // 1 Page Length pBuffer[ 3] = tracks; // 2, 3 Tracks per Zone pBuffer[11] = sectors; // 10,11 Sectors per Track pBuffer[12] = blockLength; // 12,13 Data Bytes per Physical Sector pBuffer[20] = removable; // 20 bit7 Soft Sector bit5 Removable buffer[0] = size - 1; // sense data length return std::min(currentLength, size); } keycode = SCSI::SENSE_INVALID_COMMAND_CODE; return 0; } unsigned SCSIHD::requestSense() { unsigned length = currentLength; unsigned tmpKeycode = unitAttention ? SCSI::SENSE_POWER_ON : keycode; unitAttention = false; PRT_DEBUG("Request Sense: keycode = " << tmpKeycode); keycode = SCSI::SENSE_NO_SENSE; memset(buffer + 1, 0, 17); if (length == 0) { if (mode & BIT_SCSI2) { return 0; } buffer[ 0] = (tmpKeycode >> 8) & 0xff; // Sense code length = 4; } else { buffer[ 0] = 0x70; buffer[ 2] = (tmpKeycode >> 16) & 0xff; // Sense key buffer[ 7] = 10; // Additional sense length buffer[12] = (tmpKeycode >> 8) & 0xff; // Additional sense code buffer[13] = (tmpKeycode >> 0) & 0xff; // Additional sense code qualifier length = std::min(length, 18u); } return length; } bool SCSIHD::checkReadOnly() { if (isWriteProtected()) { keycode = SCSI::SENSE_WRITE_PROTECT; return true; } return false; } unsigned SCSIHD::readCapacity() { // TODO check for overflow unsigned block = unsigned(getNbSectors()); if (block == 0) { keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT; PRT_DEBUG("hdd " << int(scsiId) << ": drive not ready"); return 0; } PRT_DEBUG("total block: " << block); --block; Endian::writeB32(&buffer[0], block); Endian::writeB32(&buffer[4], SECTOR_SIZE); // TODO is this a 32 bit field or 2x16-bit fields where the first field happens to have the value 0? return 8; } bool SCSIHD::checkAddress() { unsigned total = unsigned(getNbSectors()); if (total == 0) { keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT; PRT_DEBUG("hdd " << int(scsiId) << ": drive not ready"); return false; } if ((currentLength > 0) && (currentSector + currentLength <= total)) { return true; } PRT_DEBUG("hdd " << int(scsiId) << ": IllegalBlockAddress"); keycode = SCSI::SENSE_ILLEGAL_BLOCK_ADDRESS; return false; } // Execute scsiDeviceCheckAddress previously. unsigned SCSIHD::readSectors(unsigned& blocks) { getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true); unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE); unsigned counter = currentLength * SECTOR_SIZE; PRT_DEBUG("hdd#" << int(scsiId) << " read sector: " << currentSector << ' ' << numSectors); try { for (unsigned i = 0; i < numSectors; ++i) { auto* sbuf = aligned_cast(buffer); readSector(currentSector, sbuf[i]); ++currentSector; --currentLength; } blocks = currentLength; return counter; } catch (MSXException&) { blocks = 0; keycode = SCSI::SENSE_UNRECOVERED_READ_ERROR; return 0; } } unsigned SCSIHD::dataIn(unsigned& blocks) { if (cdb[0] == SCSI::OP_READ10) { unsigned counter = readSectors(blocks); if (counter) { return counter; } } PRT_DEBUG("dataIn error " << cdb[0]); blocks = 0; return 0; } // Execute scsiDeviceCheckAddress and scsiDeviceCheckReadOnly previously. unsigned SCSIHD::writeSectors(unsigned& blocks) { getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true); unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE); PRT_DEBUG("hdd#" << int(scsiId) << " write sector: " << currentSector << ' ' << numSectors); try { for (unsigned i = 0; i < numSectors; ++i) { auto* sbuf = aligned_cast(buffer); writeSector(currentSector, sbuf[i]); ++currentSector; --currentLength; } unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE); blocks = currentLength - tmp; unsigned counter = tmp * SECTOR_SIZE; return counter; } catch (MSXException&) { keycode = SCSI::SENSE_WRITE_FAULT; blocks = 0; return 0; } } unsigned SCSIHD::dataOut(unsigned& blocks) { if (cdb[0] == SCSI::OP_WRITE10) { return writeSectors(blocks); } PRT_DEBUG("dataOut error " << int(cdb[0])); blocks = 0; return 0; } // MBR erase only void SCSIHD::formatUnit() { if (!checkReadOnly()) { auto& sbuf = *aligned_cast(buffer); memset(&sbuf, 0, sizeof(sbuf)); try { writeSector(0, sbuf); unitAttention = true; } catch (MSXException&) { keycode = SCSI::SENSE_WRITE_FAULT; } } } byte SCSIHD::getStatusCode() { byte result = keycode ? SCSI::ST_CHECK_CONDITION : SCSI::ST_GOOD; PRT_DEBUG("SCSI status code: \n" << int(result)); return result; } unsigned SCSIHD::executeCmd(const byte* cdb_, SCSI::Phase& phase, unsigned& blocks) { PRT_DEBUG("SCSI Command: " << int(cdb[0])); memcpy(cdb, cdb_, sizeof(cdb)); message = 0; phase = SCSI::STATUS; blocks = 0; // check unit attention if (unitAttention && (mode & MODE_UNITATTENTION) && (cdb[0] != SCSI::OP_INQUIRY) && (cdb[0] != SCSI::OP_REQUEST_SENSE)) { unitAttention = false; keycode = SCSI::SENSE_POWER_ON; if (cdb[0] == SCSI::OP_TEST_UNIT_READY) { // changed = false; } PRT_DEBUG("Unit Attention. This command is not executed."); return 0; } // check LUN if (((cdb[1] & 0xe0) || lun) && (cdb[0] != SCSI::OP_REQUEST_SENSE) && !(cdb[0] == SCSI::OP_INQUIRY && !(mode & MODE_NOVAXIS))) { keycode = SCSI::SENSE_INVALID_LUN; PRT_DEBUG("check LUN error"); return 0; } if (cdb[0] != SCSI::OP_REQUEST_SENSE) { keycode = SCSI::SENSE_NO_SENSE; } if (cdb[0] < SCSI::OP_GROUP1) { currentSector = ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3]; currentLength = cdb[4]; switch (cdb[0]) { case SCSI::OP_TEST_UNIT_READY: PRT_DEBUG("TestUnitReady"); return 0; case SCSI::OP_INQUIRY: { PRT_DEBUG("Inquiry " << currentLength); unsigned counter = inquiry(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_REQUEST_SENSE: { PRT_DEBUG("RequestSense"); unsigned counter = requestSense(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_READ6: PRT_DEBUG("Read6: " << currentSector << ' ' << currentLength); if (currentLength == 0) { currentLength = SECTOR_SIZE / 2; } if (checkAddress()) { unsigned counter = readSectors(blocks); if (counter) { cdb[0] = SCSI::OP_READ10; phase = SCSI::DATA_IN; return counter; } } return 0; case SCSI::OP_WRITE6: PRT_DEBUG("Write6: " << currentSector << ' ' << currentLength); if (currentLength == 0) { currentLength = SECTOR_SIZE / 2; } if (checkAddress() && !checkReadOnly()) { getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true); unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE); blocks = currentLength - tmp; unsigned counter = tmp * SECTOR_SIZE; cdb[0] = SCSI::OP_WRITE10; phase = SCSI::DATA_OUT; return counter; } return 0; case SCSI::OP_SEEK6: PRT_DEBUG("Seek6: " << currentSector); getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true); currentLength = 1; checkAddress(); return 0; case SCSI::OP_MODE_SENSE: { PRT_DEBUG("ModeSense: " << currentLength); unsigned counter = modeSense(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_FORMAT_UNIT: PRT_DEBUG("FormatUnit"); formatUnit(); return 0; case SCSI::OP_START_STOP_UNIT: PRT_DEBUG("StartStopUnit (Not supported for this device.)"); return 0; case SCSI::OP_REZERO_UNIT: case SCSI::OP_REASSIGN_BLOCKS: case SCSI::OP_RESERVE_UNIT: case SCSI::OP_RELEASE_UNIT: case SCSI::OP_SEND_DIAGNOSTIC: PRT_DEBUG("SCSI_Group0 dummy"); return 0; } } else { currentSector = Endian::read_UA_B32(&cdb[2]); currentLength = Endian::read_UA_B16(&cdb[7]); switch (cdb[0]) { case SCSI::OP_READ10: PRT_DEBUG("Read10: " << currentSector << ' ' << currentLength); if (checkAddress()) { unsigned counter = readSectors(blocks); if (counter) { phase = SCSI::DATA_IN; return counter; } } return 0; case SCSI::OP_WRITE10: PRT_DEBUG("Write10: " << currentSector << ' ' << currentLength); if (checkAddress() && !checkReadOnly()) { unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE); blocks = currentLength - tmp; unsigned counter = tmp * SECTOR_SIZE; phase = SCSI::DATA_OUT; return counter; } return 0; case SCSI::OP_READ_CAPACITY: { PRT_DEBUG("ReadCapacity"); unsigned counter = readCapacity(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_SEEK10: PRT_DEBUG("Seek10: " << currentSector); getMotherBoard().getLedStatus().setLed(LedStatus::FDD, true); currentLength = 1; checkAddress(); return 0; } } PRT_DEBUG("unsupported command " << cdb[0]); keycode = SCSI::SENSE_INVALID_COMMAND_CODE; return 0; } unsigned SCSIHD::executingCmd(SCSI::Phase& phase, unsigned& blocks) { phase = SCSI::EXECUTE; blocks = 0; return 0; // Always for non-CD-ROM it seems } byte SCSIHD::msgIn() { byte result = message; message = 0; //PRT_DEBUG("SCSIDevice " << int(scsiId) << " msgIn returning " << result); return result; } /* scsiDeviceMsgOut() Notes: [out] -1: Busfree demand. (Please process it in the call origin.) bit2: Status phase demand. Error happend. bit1: Make it to a busfree if ATN has not been released. bit0: There is a message(MsgIn). */ int SCSIHD::msgOut(byte value) { PRT_DEBUG("SCSI #" << int(scsiId) << " message out: " << int(value)); if (value & 0x80) { lun = value & 7; return 0; } switch (value) { case SCSI::MSG_INITIATOR_DETECT_ERROR: keycode = SCSI::SENSE_INITIATOR_DETECTED_ERR; return 6; case SCSI::MSG_BUS_DEVICE_RESET: busReset(); // fall-through case SCSI::MSG_ABORT: return -1; case SCSI::MSG_REJECT: case SCSI::MSG_PARITY_ERROR: case SCSI::MSG_NO_OPERATION: return 2; } message = SCSI::MSG_REJECT; return ((value >= 0x04) && (value <= 0x11)) ? 3 : 1; } template void SCSIHD::serialize(Archive& ar, unsigned /*version*/) { // don't serialize SCSIDevice, SectorAccessibleDisk, DiskContainer // base classes ar.template serializeBase(*this); ar.serialize("keycode", keycode); ar.serialize("currentSector", currentSector); ar.serialize("currentLength", currentLength); ar.serialize("unitAttention", unitAttention); ar.serialize("message", message); ar.serialize("lun", lun); ar.serialize_blob("cdb", cdb, sizeof(cdb)); } INSTANTIATE_SERIALIZE_METHODS(SCSIHD); REGISTER_POLYMORPHIC_INITIALIZER(SCSIDevice, SCSIHD, "SCSIHD"); } // namespace openmsx openmsx-0.10.0/src/ide/CDImageCLI.hh0000644000175000017500000000067612262345041017507 0ustar manuelmanuel00000000000000#ifndef CDIMAGECLI_HH #define CDIMAGECLI_HH #include "CLIOption.hh" namespace openmsx { class CommandLineParser; class CDImageCLI : public CLIOption { public: explicit CDImageCLI(CommandLineParser& cmdLineParser); virtual void parseOption(const std::string& option, std::deque& cmdLine); virtual string_ref optionHelp() const; private: CommandLineParser& parser; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/HD.hh0000644000175000017500000000322412262345041016211 0ustar manuelmanuel00000000000000#ifndef HD_HH #define HD_HH #include "Filename.hh" #include "SectorAccessibleDisk.hh" #include "DiskContainer.hh" #include "TigerTree.hh" #include "serialize_meta.hh" #include "openmsx.hh" #include #include namespace openmsx { class MSXMotherBoard; class HDCommand; class File; class DeviceConfig; class HD : public SectorAccessibleDisk, public DiskContainer , public TTData { public: explicit HD(const DeviceConfig& config); virtual ~HD(); const std::string& getName() const; const Filename& getImageName() const; void switchImage(const Filename& filename); std::string getTigerTreeHash(); template void serialize(Archive& ar, unsigned version); MSXMotherBoard& getMotherBoard() const { return motherBoard; } private: // SectorAccessibleDisk: virtual void readSectorImpl (size_t sector, SectorBuffer& buf); virtual void writeSectorImpl(size_t sector, const SectorBuffer& buf); virtual size_t getNbSectorsImpl() const; virtual bool isWriteProtectedImpl() const; virtual Sha1Sum getSha1Sum(); // Diskcontainer: virtual SectorAccessibleDisk* getSectorAccessibleDisk(); virtual const std::string& getContainerName() const; virtual bool diskChanged(); virtual int insertDisk(const std::string& filename); // TTData virtual uint8_t* getData(size_t offset, size_t size); void openImage(); MSXMotherBoard& motherBoard; std::string name; std::unique_ptr hdCommand; std::unique_ptr tigerTree; std::unique_ptr file; Filename filename; size_t filesize; bool alreadyTried; }; REGISTER_BASE_CLASS(HD, "HD"); SERIALIZE_CLASS_VERSION(HD, 2); } // namespace openmsx #endif openmsx-0.10.0/src/ide/DummyIDEDevice.cc0000644000175000017500000000144412262345041020443 0ustar manuelmanuel00000000000000#include "DummyIDEDevice.hh" #include "serialize.hh" namespace openmsx { void DummyIDEDevice::reset(EmuTime::param /*time*/) { // do nothing } word DummyIDEDevice::readData(EmuTime::param /*time*/) { return 0x7F7F; } byte DummyIDEDevice::readReg(nibble /*reg*/, EmuTime::param /*time*/) { return 0x7F; } void DummyIDEDevice::writeData(word /*value*/, EmuTime::param /*time*/) { // do nothing } void DummyIDEDevice::writeReg(nibble /*reg*/, byte /*value*/, EmuTime::param /*time*/) { // do nothing } template void DummyIDEDevice::serialize(Archive& /*ar*/, unsigned /*version*/) { // nothing } INSTANTIATE_SERIALIZE_METHODS(DummyIDEDevice); REGISTER_POLYMORPHIC_INITIALIZER(IDEDevice, DummyIDEDevice, "DummyIDEDevice"); } // namespace openmsx openmsx-0.10.0/src/ide/CDImageCLI.cc0000644000175000017500000000175412262345041017473 0ustar manuelmanuel00000000000000#include "CDImageCLI.hh" #include "CommandLineParser.hh" #include "GlobalCommandController.hh" #include "TclObject.hh" #include "MSXException.hh" using std::deque; using std::string; namespace openmsx { CDImageCLI::CDImageCLI(CommandLineParser& parser_) : parser(parser_) { parser.registerOption("-cda", *this); // TODO: offer more options in case you want to specify 2 hard disk images? } void CDImageCLI::parseOption(const string& option, deque& cmdLine) { string_ref cd = string_ref(option).substr(1); // cda string filename = getArgument(option, cmdLine); if (!parser.getGlobalCommandController().hasCommand(cd)) { // TODO WIP throw MSXException("No CDROM named '" + cd + "'."); } TclObject command(parser.getGlobalCommandController().getInterpreter()); command.addListElement(cd); command.addListElement(filename); command.executeCommand(); } string_ref CDImageCLI::optionHelp() const { return "Use iso image in argument for the CDROM extension"; } } // namespace openmsx openmsx-0.10.0/src/ide/DummySCSIDevice.hh0000644000175000017500000000143412262345041020614 0ustar manuelmanuel00000000000000#ifndef DUMMYSCSIDEVICE_HH #define DUMMYSCSIDEVICE_HH #include "SCSIDevice.hh" #include "serialize_meta.hh" namespace openmsx { class DummySCSIDevice : public SCSIDevice { public: virtual void reset(); virtual bool isSelected(); virtual unsigned executeCmd(const byte* cdb, SCSI::Phase& phase, unsigned& blocks); virtual unsigned executingCmd(SCSI::Phase& phase, unsigned& blocks); virtual byte getStatusCode(); virtual int msgOut(byte value); virtual byte msgIn(); virtual void disconnect(); virtual void busReset(); // only used in MB89352 controller virtual unsigned dataIn(unsigned& blocks); virtual unsigned dataOut(unsigned& blocks); template void serialize(Archive& ar, unsigned version); }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/MB89352.cc0000644000175000017500000004557612262345041016635 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/MB89352.c,v ** Revision: 1.9 ** Date: 2007/03/28 17:35:35 ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, white cat */ /* * Notes: * Not suppport padding transfer and interrupt signal. (Not used MEGA-SCSI) * Message system might be imperfect. (Not used in MEGA-SCSI usually) */ #include "MB89352.hh" #include "SCSIDevice.hh" #include "DummySCSIDevice.hh" #include "SCSIHD.hh" #include "SCSILS120.hh" #include "DeviceConfig.hh" #include "XMLElement.hh" #include "MSXException.hh" #include "StringOp.hh" #include "serialize.hh" #include "memory.hh" #include #include #include using std::string; namespace openmsx { static const byte REG_BDID = 0; // Bus Device ID (r/w) static const byte REG_SCTL = 1; // Spc Control (r/w) static const byte REG_SCMD = 2; // Command (r/w) static const byte REG_OPEN = 3; // (open) static const byte REG_INTS = 4; // Interrupt Sense (r/w) static const byte REG_PSNS = 5; // Phase Sense (r) static const byte REG_SDGC = 5; // SPC Diag. Control (w) static const byte REG_SSTS = 6; // SPC SCSI::STATUS (r) static const byte REG_SERR = 7; // SPC Error SCSI::STATUS (r/w?) static const byte REG_PCTL = 8; // Phase Control (r/w) static const byte REG_MBC = 9; // Modified Byte Counter(r) static const byte REG_DREG = 10; // Data Register (r/w) static const byte REG_TEMP = 11; // Temporary Register (r/w) // Another value is maintained respec- // tively for writing and for reading static const byte REG_TCH = 12; // Transfer Counter High(r/w) static const byte REG_TCM = 13; // Transfer Counter Mid (r/w) static const byte REG_TCL = 14; // Transfer Counter Low (r/w) static const byte REG_TEMPWR = 13; // (TEMP register preservation place for writing) static const byte FIX_PCTL = 14; // (REG_PCTL & 7) static const byte PSNS_IO = 0x01; static const byte PSNS_CD = 0x02; static const byte PSNS_MSG = 0x04; static const byte PSNS_BSY = 0x08; static const byte PSNS_SEL = 0x10; static const byte PSNS_ATN = 0x20; static const byte PSNS_ACK = 0x40; static const byte PSNS_REQ = 0x80; static const byte PSNS_SELECTION = PSNS_SEL; static const byte PSNS_COMMAND = PSNS_CD; static const byte PSNS_DATAIN = PSNS_IO; static const byte PSNS_DATAOUT = 0; static const byte PSNS_STATUS = PSNS_CD | PSNS_IO; static const byte PSNS_MSGIN = PSNS_MSG | PSNS_CD | PSNS_IO; static const byte PSNS_MSGOUT = PSNS_MSG | PSNS_CD; static const byte INTS_ResetCondition = 0x01; static const byte INTS_SPC_HardError = 0x02; static const byte INTS_TimeOut = 0x04; static const byte INTS_ServiceRequited = 0x08; static const byte INTS_CommandComplete = 0x10; static const byte INTS_Disconnected = 0x20; static const byte INTS_ReSelected = 0x40; static const byte INTS_Selected = 0x80; static const byte CMD_BusRelease = 0x00; static const byte CMD_Select = 0x20; static const byte CMD_ResetATN = 0x40; static const byte CMD_SetATN = 0x60; static const byte CMD_Transfer = 0x80; static const byte CMD_TransferPause = 0xA0; static const byte CMD_Reset_ACK_REQ = 0xC0; static const byte CMD_Set_ACK_REQ = 0xE0; static const byte CMD_MASK = 0xE0; static const unsigned MAX_DEV = 8; MB89352::MB89352(const DeviceConfig& config) { PRT_DEBUG("spc create"); // TODO: devBusy = false; // ALMOST COPY PASTED FROM WD33C93: for (auto* t : config.getXML()->getChildren("target")) { unsigned id = t->getAttributeAsInt("id"); if (id >= MAX_DEV) { throw MSXException(StringOp::Builder() << "Invalid SCSI id: " << id << " (should be 0.." + MAX_DEV - 1 << ')'); } if (dev[id]) { throw MSXException(StringOp::Builder() << "Duplicate SCSI id: " << id); } DeviceConfig conf(config, *t); auto& type = t->getChild("type").getData(); if (type == "SCSIHD") { dev[id] = make_unique(conf, buffer, SCSIDevice::MODE_SCSI2 | SCSIDevice::MODE_MEGASCSI); } else if (type == "SCSILS120") { dev[id] = make_unique(conf, buffer, SCSIDevice::MODE_SCSI2 | SCSIDevice::MODE_MEGASCSI); } else { throw MSXException("Unknown SCSI device: " + type); } } // fill remaining targets with dummy SCSI devices to prevent crashes for (unsigned i = 0; i < MAX_DEV; ++i) { if (!dev[i]) { dev[i] = make_unique(); } } reset(false); // avoid UMR on savestate memset(buffer.data(), 0, SCSIDevice::BUFFER_SIZE); msgin = 0; blockCounter = 0; nextPhase = SCSI::UNDEFINED; targetId = 0; } MB89352::~MB89352() { } void MB89352::disconnect() { if (phase != SCSI::BUS_FREE) { assert(targetId < MAX_DEV); dev[targetId]->disconnect(); regs[REG_INTS] |= INTS_Disconnected; phase = SCSI::BUS_FREE; nextPhase = SCSI::UNDEFINED; } regs[REG_PSNS] = 0; isBusy = false; isTransfer = false; counter = 0; tc = 0; atn = 0; } void MB89352::softReset() { isEnabled = false; for (int i = 2; i < 15; ++i) { regs[i] = 0; } regs[15] = 0xFF; // un mapped memset(cdb, 0, sizeof(cdb)); cdbIdx = 0; bufIdx = 0; phase = SCSI::BUS_FREE; disconnect(); } void MB89352::reset(bool scsireset) { //PRT_DEBUG("MB89352 reset"); regs[REG_BDID] = 0x80; // Initial value regs[REG_SCTL] = 0x80; rst = false; atn = 0; myId = 7; softReset(); if (scsireset) { for (byte i = 0; i < MAX_DEV; ++i) { dev[i]->reset(); } } } void MB89352::setACKREQ(byte& value) { // REQ check if ((regs[REG_PSNS] & (PSNS_REQ | PSNS_BSY)) != (PSNS_REQ | PSNS_BSY)) { PRT_DEBUG("set ACK/REQ: REQ/BSY check error"); if (regs[REG_PSNS] & PSNS_IO) { // SCSI -> SPC value = 0xFF; } return; } // phase check if (regs[FIX_PCTL] != (regs[REG_PSNS] & 7)) { PRT_DEBUG("set ACK/REQ: phase check error"); if (regs[REG_PSNS] & PSNS_IO) { // SCSI -> SPC value = 0xFF; } if (isTransfer) { regs[REG_INTS] |= INTS_ServiceRequited; } return; } switch (phase) { case SCSI::DATA_IN: // Transfer phase (data in) value = buffer[bufIdx]; ++bufIdx; regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_DATAIN; break; case SCSI::DATA_OUT: // Transfer phase (data out) buffer[bufIdx] = value; ++bufIdx; regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_DATAOUT; break; case SCSI::COMMAND: // Command phase if (counter < 0) { // Initialize command routine cdbIdx = 0; counter = (value < 0x20) ? 6 : ((value < 0xA0) ? 10 : 12); } cdb[cdbIdx] = value; ++cdbIdx; regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_COMMAND; break; case SCSI::STATUS: // SCSI::STATUS phase value = dev[targetId]->getStatusCode(); regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_STATUS; break; case SCSI::MSG_IN: // Message In phase value = dev[targetId]->msgIn(); regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_MSGIN; break; case SCSI::MSG_OUT: // Message Out phase msgin |= dev[targetId]->msgOut(value); regs[REG_PSNS] = PSNS_ACK | PSNS_BSY | PSNS_MSGOUT; break; default: PRT_DEBUG("set ACK/REQ code error"); break; } } void MB89352::resetACKREQ() { // ACK check if ((regs[REG_PSNS] & (PSNS_ACK | PSNS_BSY)) != (PSNS_ACK | PSNS_BSY)) { PRT_DEBUG("reset ACK/REQ: ACK/BSY check error"); return; } // phase check if (regs[FIX_PCTL] != (regs[REG_PSNS] & 7)) { PRT_DEBUG("reset ACK/REQ: phase check error"); if (isTransfer) { regs[REG_INTS] |= INTS_ServiceRequited; } return; } switch (phase) { case SCSI::DATA_IN: // Transfer phase (data in) if (--counter > 0) { regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN; } else { if (blockCounter > 0) { counter = dev[targetId]->dataIn(blockCounter); if (counter) { regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN; bufIdx = 0; break; } } regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS; phase = SCSI::STATUS; } break; case SCSI::DATA_OUT: // Transfer phase (data out) if (--counter > 0) { regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT; } else { counter = dev[targetId]->dataOut(blockCounter); if (counter) { regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT; bufIdx = 0; break; } regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS; phase = SCSI::STATUS; } break; case SCSI::COMMAND: // Command phase if (--counter > 0) { regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_COMMAND; } else { bufIdx = 0; // reset buffer index // TODO: devBusy = true; counter = dev[targetId]->executeCmd(cdb, phase, blockCounter); switch (phase) { case SCSI::DATA_IN: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN; break; case SCSI::DATA_OUT: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT; break; case SCSI::STATUS: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS; break; case SCSI::EXECUTE: regs[REG_PSNS] = PSNS_BSY; return; // note: return iso break default: PRT_DEBUG("phase error"); break; } // TODO: devBusy = false; } break; case SCSI::STATUS: // SCSI::STATUS phase regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGIN; phase = SCSI::MSG_IN; break; case SCSI::MSG_IN: // Message In phase if (msgin <= 0) { disconnect(); break; } msgin = 0; // throw to SCSI::MSG_OUT... case SCSI::MSG_OUT: // Message Out phase if (msgin == -1) { disconnect(); return; } if (atn) { if (msgin & 2) { disconnect(); return; } regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT; return; } if (msgin & 1) { phase = SCSI::MSG_IN; } else { if (msgin & 4) { phase = SCSI::STATUS; nextPhase = SCSI::UNDEFINED; } else { phase = nextPhase; nextPhase = SCSI::UNDEFINED; } } msgin = 0; switch (phase) { case SCSI::COMMAND: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_COMMAND; break; case SCSI::DATA_IN: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN; break; case SCSI::DATA_OUT: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT; break; case SCSI::STATUS: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS; break; case SCSI::MSG_IN: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGIN; break; default: PRT_DEBUG("MsgOut code error"); break; } return; default: //UNREACHABLE; PRT_DEBUG("reset ACK/REQ code error"); break; } if (atn) { nextPhase = phase; phase = SCSI::MSG_OUT; regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT; } } byte MB89352::readDREG() { if (isTransfer && (tc > 0)) { setACKREQ(regs[REG_DREG]); resetACKREQ(); //PRT_DEBUG("DREG read: " << tc << ' ' << std::hex << (int)regs[REG_DREG]); --tc; if (tc == 0) { isTransfer = false; regs[REG_INTS] |= INTS_CommandComplete; } regs[REG_MBC] = (regs[REG_MBC] - 1) & 0x0F; return regs[REG_DREG]; } else { return 0xFF; } } void MB89352::writeDREG(byte value) { if (isTransfer && (tc > 0)) { //PRT_DEBUG("DREG write: " << tc << ' ' << std::hex << (int)value); setACKREQ(value); resetACKREQ(); --tc; if (tc == 0) { isTransfer = false; regs[REG_INTS] |= INTS_CommandComplete; } regs[REG_MBC] = (regs[REG_MBC] - 1) & 0x0F; } } void MB89352::writeRegister(byte reg, byte value) { //PRT_DEBUG("SPC write register: " << std::hex << (int)reg << ' ' << std::hex << (int)value); switch (reg) { case REG_DREG: // write data Register writeDREG(value); break; case REG_SCMD: { if (!isEnabled) { break; } // bus reset if (value & 0x10) { if (((regs[REG_SCMD] & 0x10) == 0) & (regs[REG_SCTL] == 0)) { PRT_DEBUG("SPC: bus reset"); rst = true; regs[REG_INTS] |= INTS_ResetCondition; for (byte i = 0; i < MAX_DEV; ++i) { dev[i]->busReset(); } disconnect(); // alternative routine } } else { rst = false; } regs[REG_SCMD] = value; // execute spc command switch (value & CMD_MASK) { case CMD_Set_ACK_REQ: switch (phase) { case SCSI::DATA_IN: case SCSI::STATUS: case SCSI::MSG_IN: setACKREQ(regs[REG_TEMP]); break; default: setACKREQ(regs[REG_TEMPWR]); } break; case CMD_Reset_ACK_REQ: resetACKREQ(); break; case CMD_Select: { if (rst) { regs[REG_INTS] |= INTS_TimeOut; break; } if (regs[REG_PCTL] & 1) { PRT_DEBUG("reselection error " << std::hex << int(regs[REG_TEMPWR])); regs[REG_INTS] |= INTS_TimeOut; disconnect(); break; } bool err = false; int x = regs[REG_BDID] & regs[REG_TEMPWR]; if (phase == SCSI::BUS_FREE && x && x != regs[REG_TEMPWR]) { x = regs[REG_TEMPWR] & ~regs[REG_BDID]; // the targetID is calculated. // It is given priority that the number is large. for (targetId = 0; targetId < MAX_DEV; ++targetId) { x >>= 1; if (x == 0) { break; } } if (/*!TODO: devBusy &&*/ dev[targetId]->isSelected()) { PRT_DEBUG("selection OK of target " << int(targetId)); regs[REG_INTS] |= INTS_CommandComplete; isBusy = true; msgin = 0; counter = -1; err = false; if (atn) { phase = SCSI::MSG_OUT; nextPhase = SCSI::COMMAND; regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT; } else { phase = SCSI::COMMAND; nextPhase = SCSI::UNDEFINED; regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_COMMAND; } } else { err = true; } } else { err = true; } if (err) { PRT_DEBUG("selection error on target " << int(targetId)); regs[REG_INTS] |= INTS_TimeOut; disconnect(); } break; } // hardware transfer case CMD_Transfer: PRT_DEBUG("CMD_Transfer " << tc << " ( " << (tc/512) << ')'); if ((regs[FIX_PCTL] == (regs[REG_PSNS] & 7)) && (regs[REG_PSNS] & (PSNS_REQ | PSNS_BSY))) { isTransfer = true; // set Xfer in Progress } else { regs[REG_INTS] |= INTS_ServiceRequited; PRT_DEBUG("phase error"); } break; case CMD_BusRelease: PRT_DEBUG("CMD_BusRelease"); disconnect(); break; case CMD_SetATN: PRT_DEBUG("CMD_SetATN"); atn = PSNS_ATN; break; case CMD_ResetATN: PRT_DEBUG("CMD_ResetATN"); atn = 0; break; case CMD_TransferPause: // nothing is done in the initiator. PRT_DEBUG("CMD_TransferPause"); break; } break; // end of REG_SCMD } case REG_INTS: // Reset Interrupts regs[REG_INTS] &= ~value; if (rst) { regs[REG_INTS] |= INTS_ResetCondition; } //PRT_DEBUG2("INTS reset: %x %x\n", value, regs[REG_INTS]); break; case REG_TEMP: regs[REG_TEMPWR] = value; break; case REG_TCL: tc = (tc & 0xFFFF00) + (value << 0); //PRT_DEBUG1("set tcl: %d\n", tc); break; case REG_TCM: tc = (tc & 0xFF00FF) + (value << 8); //PRT_DEBUG1("set tcm: %d\n", tc); break; case REG_TCH: tc = (tc & 0x00FFFF) + (value << 16); //PRT_DEBUG1("set tch: %d\n", tc); break; case REG_PCTL: regs[REG_PCTL] = value; regs[FIX_PCTL] = value & 7; break; case REG_BDID: // set Bus Device ID value &= 7; myId = value; regs[REG_BDID] = 1 << value; break; // Nothing case REG_SDGC: case REG_SSTS: case REG_SERR: case REG_MBC: case 15: break; case REG_SCTL: { bool flag = !(value & 0xE0); if (flag != isEnabled) { isEnabled = flag; if (!flag) { softReset(); } } // throw to default } default: regs[reg] = value; } } byte MB89352::getSSTS() const { byte result = 1; // set fifo empty if (isTransfer) { if (regs[REG_PSNS] & PSNS_IO) { // SCSI -> SPC transfer if (tc >= 8) { result = 2; // set fifo full } else { if (tc != 0) { result = 0; // set fifo 1..7 bytes } } } } if (phase != SCSI::BUS_FREE) { result |= 0x80; // set iniciator } if (isBusy) { result |= 0x20; // set SPC_BSY } if ((phase >= SCSI::COMMAND) || isTransfer) { result |= 0x10; // set Xfer in Progress } if (rst) { result |= 0x08; // set SCSI RST } if (tc == 0) { result |= 0x04; // set tc = 0 } return result; } byte MB89352::readRegister(byte reg) { //PRT_DEBUG("MB89352: Read register " << (int)reg); byte result; switch (reg) { case REG_DREG: result = readDREG(); break; case REG_PSNS: if (phase == SCSI::EXECUTE) { counter = dev[targetId]->executingCmd(phase, blockCounter); if (atn && phase != SCSI::EXECUTE) { nextPhase = phase; phase = SCSI::MSG_OUT; regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_MSGOUT; } else { switch (phase) { case SCSI::DATA_IN: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAIN; break; case SCSI::DATA_OUT: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_DATAOUT; break; case SCSI::STATUS: regs[REG_PSNS] = PSNS_REQ | PSNS_BSY | PSNS_STATUS; break; case SCSI::EXECUTE: regs[REG_PSNS] = PSNS_BSY; break; default: PRT_DEBUG("phase error"); break; } } } result = regs[REG_PSNS] | atn; break; default: result = peekRegister(reg); } //PRT_DEBUG2("SPC reg read: %x %x\n", reg, result); return result; } byte MB89352::peekDREG() const { if (isTransfer && (tc > 0)) { return regs[REG_DREG]; } else { return 0xFF; } } byte MB89352::peekRegister(byte reg) const { switch (reg) { case REG_DREG: return peekDREG(); case REG_PSNS: return regs[REG_PSNS] | atn; case REG_SSTS: return getSSTS(); case REG_TCH: return (tc >> 16) & 0xFF; case REG_TCM: return (tc >> 8) & 0xFF; case REG_TCL: return (tc >> 0) & 0xFF; default: return regs[reg]; } } // TODO duplicated in WD33C93.cc static enum_string phaseInfo[] = { { "UNDEFINED", SCSI::UNDEFINED }, { "BUS_FREE", SCSI::BUS_FREE }, { "ARBITRATION", SCSI::ARBITRATION }, { "SELECTION", SCSI::SELECTION }, { "RESELECTION", SCSI::RESELECTION }, { "COMMAND", SCSI::COMMAND }, { "EXECUTE", SCSI::EXECUTE }, { "DATA_IN", SCSI::DATA_IN }, { "DATA_OUT", SCSI::DATA_OUT }, { "STATUS", SCSI::STATUS }, { "MSG_OUT", SCSI::MSG_OUT }, { "MSG_IN", SCSI::MSG_IN } }; SERIALIZE_ENUM(SCSI::Phase, phaseInfo); template void MB89352::serialize(Archive& ar, unsigned /*version*/) { ar.serialize_blob("buffer", buffer.data(), buffer.size()); char tag[8] = { 'd', 'e', 'v', 'i', 'c', 'e', 'X', 0 }; for (unsigned i = 0; i < MAX_DEV; ++i) { tag[6] = char('0' + i); ar.serializePolymorphic(tag, *dev[i]); } ar.serialize("bufIdx", bufIdx); ar.serialize("msgin", msgin); ar.serialize("counter", counter); ar.serialize("blockCounter", blockCounter); ar.serialize("tc", tc); ar.serialize("phase", phase); ar.serialize("nextPhase", nextPhase); ar.serialize("myId", myId); ar.serialize("targetId", targetId); ar.serialize_blob("registers", regs, sizeof(regs)); ar.serialize("rst", rst); ar.serialize("atn", atn); ar.serialize("isEnabled", isEnabled); ar.serialize("isBusy", isBusy); ar.serialize("isTransfer", isTransfer); ar.serialize("cdbIdx", cdbIdx); ar.serialize_blob("cdb", cdb, sizeof(cdb)); } INSTANTIATE_SERIALIZE_METHODS(MB89352); } // namespace openmsx openmsx-0.10.0/src/ide/HDImageCLI.hh0000644000175000017500000000067612262345041017514 0ustar manuelmanuel00000000000000#ifndef HDIMAGECLI_HH #define HDIMAGECLI_HH #include "CLIOption.hh" namespace openmsx { class CommandLineParser; class HDImageCLI : public CLIOption { public: explicit HDImageCLI(CommandLineParser& cmdLineParser); virtual void parseOption(const std::string& option, std::deque& cmdLine); virtual string_ref optionHelp() const; private: CommandLineParser& parser; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/IDEDeviceFactory.hh0000644000175000017500000000040612262345041020766 0ustar manuelmanuel00000000000000#ifndef IDEDEVICEFACTORY_HH #define IDEDEVICEFACTORY_HH #include namespace openmsx { class IDEDevice; class DeviceConfig; namespace IDEDeviceFactory { std::unique_ptr create(const DeviceConfig& config); } } // namespace openmsx #endif openmsx-0.10.0/src/ide/IDEDevice.hh0000644000175000017500000000074712262345041017446 0ustar manuelmanuel00000000000000#ifndef IDEDEVICE_HH #define IDEDEVICE_HH #include "EmuTime.hh" #include "openmsx.hh" namespace openmsx { class IDEDevice { public: virtual ~IDEDevice() {} virtual void reset(EmuTime::param time) = 0; virtual word readData(EmuTime::param time) = 0; virtual byte readReg(nibble reg, EmuTime::param time) = 0; virtual void writeData(word value, EmuTime::param time) = 0; virtual void writeReg(nibble reg, byte value, EmuTime::param time) = 0; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/DummyIDEDevice.hh0000644000175000017500000000106012262345041020447 0ustar manuelmanuel00000000000000#ifndef DUMMYIDEDEVICE_HH #define DUMMYIDEDEVICE_HH #include "IDEDevice.hh" #include "serialize_meta.hh" namespace openmsx { class DummyIDEDevice : public IDEDevice { public: virtual void reset(EmuTime::param time); virtual word readData(EmuTime::param time); virtual byte readReg(nibble reg, EmuTime::param time); virtual void writeData(word value, EmuTime::param time); virtual void writeReg(nibble reg, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/MegaSCSI.cc0000644000175000017500000001202712262345041017240 0ustar manuelmanuel00000000000000/* * MEGA-SCSI and ESE-RAM cartridge: * The mapping does SRAM and MB89352A(MEGA-SCSI) to ASCII8 or * an interchangeable bank controller. * * Specification: * SRAM(MegaROM) controller: ASCII8 type * SRAM capacity : 128, 256, 512 and 1024KB * SCSI Protocol Controller: Fujitsu MB89352A * * Bank changing address: * bank 4(0x4000-0x5fff): 0x6000 - 0x67FF (0x6000 used) * bank 6(0x6000-0x7fff): 0x6800 - 0x6FFF (0x6800 used) * bank 8(0x8000-0x9fff): 0x7000 - 0x77FF (0x7000 used) * bank A(0xa000-0xbfff): 0x7800 - 0x7FFF (0x7800 used) * * ESE-RAM Bank Map: * BANK 00H-7FH (read only) * BANK 80H-FFH (write and read. mirror of 00H-7FH) * * MEGA-SCSI Bank Map: * BANK 00H-3FH (sram read only. mirror of 80H-BFH) * BANK 40H-7EH (mirror of 7FH. Use is prohibited) * BANK 7FH (SPC) * BANK 80H-FFH (sram write and read) * * SPC Bank: * 0x0000 - 0x0FFF : * SPC Data register r/w (mirror of all 0x1FFA) * 0x1000 - 0x1FEF : * mirror of 0x1FF0 - 0x1FFF * Use is prohibited about the image * 0x1FF0 - 0x1FFE : * SPC register * 0x1FFF : * un mapped * * Note: * It is possible to access it by putting it out to 8000H - BFFFH * though the SPC bank is arranged in chiefly 4000H-5FFF. */ #include "MegaSCSI.hh" #include "RomBlockDebuggable.hh" #include "MB89352.hh" #include "SRAM.hh" #include "StringOp.hh" #include "MSXException.hh" #include "serialize.hh" #include "memory.hh" #include namespace openmsx { static const byte SPC = 0x7F; static std::unique_ptr createSRAM( const DeviceConfig& config, const std::string& name) { unsigned sramSize = config.getChildDataAsInt("sramsize", 1024); // size in kb if (sramSize != 1024 && sramSize != 512 && sramSize != 256 && sramSize != 128) { throw MSXException(StringOp::Builder() << "SRAM size for " << name << " should be 128, 256, 512 or 1024kB and not " << sramSize << "kB!"); } sramSize *= 1024; // in bytes return make_unique(name + " SRAM", sramSize, config); } MegaSCSI::MegaSCSI(const DeviceConfig& config) : MSXDevice(config) , mb89352(make_unique(config)) , sram(createSRAM(config, getName())) , romBlockDebug(make_unique( *this, mapped, 0x4000, 0x8000, 13)) , blockMask((sram->getSize() / 0x2000) - 1) { } MegaSCSI::~MegaSCSI() { } void MegaSCSI::reset(EmuTime::param /*time*/) { for (int i = 0; i < 4; ++i) { setSRAM(i, 0); } mb89352->reset(true); } byte MegaSCSI::readMem(word address, EmuTime::param /*time*/) { byte result; if ((0x4000 <= address) && (address < 0xC000)) { unsigned page = (address / 0x2000) - 2; word addr = address & 0x1FFF; if (mapped[page] == SPC) { // SPC read if (addr < 0x1000) { // Data Register result = mb89352->readDREG(); } else { result = mb89352->readRegister(addr & 0x0F); } } else { result = (*sram)[0x2000 * mapped[page] + addr]; } } else { result = 0xFF; } return result; } byte MegaSCSI::peekmem(word address, EmuTime::param /*time*/) const { if (const byte* cacheline = MegaSCSI::getReadCacheLine(address)) { return *cacheline; } else { address &= 0x1FFF; if (address < 0x1000) { return mb89352->peekDREG(); } else { return mb89352->peekRegister(address & 0x0F); } } } const byte* MegaSCSI::getReadCacheLine(word address) const { if ((0x4000 <= address) && (address < 0xC000)) { unsigned page = (address / 0x2000) - 2; address &= 0x1FFF; if (mapped[page] == SPC) { return nullptr; } else { return &(*sram)[0x2000 * mapped[page] + address]; } } else { return unmappedRead; } } void MegaSCSI::writeMem(word address, byte value, EmuTime::param /*time*/) { if ((0x6000 <= address) && (address < 0x8000)) { byte region = ((address >> 11) & 3); setSRAM(region, value); } else if ((0x4000 <= address) && (address < 0xC000)) { unsigned page = (address / 0x2000) - 2; address &= 0x1FFF; if (mapped[page] == SPC) { if (address < 0x1000) { mb89352->writeDREG(value); } else { mb89352->writeRegister(address & 0x0F, value); } } else if (isWriteable[page]) { sram->write(0x2000 * mapped[page] + address, value); } } } byte* MegaSCSI::getWriteCacheLine(word address) const { if ((0x6000 <= address) && (address < 0x8000)) { return nullptr; } else if ((0x4000 <= address) && (address < 0xC000)) { unsigned page = (address / 0x2000) - 2; if (mapped[page] == SPC) { return nullptr; } else if (isWriteable[page]) { return nullptr; } } return unmappedWrite; } void MegaSCSI::setSRAM(unsigned region, byte block) { invalidateMemCache(region * 0x2000 + 0x4000, 0x2000); assert(region < 4); isWriteable[region] = (block & 0x80) != 0; mapped[region] = ((block & 0xC0) == 0x40) ? 0x7F : (block & blockMask); } template void MegaSCSI::serialize(Archive& ar, unsigned /*version*/) { ar.serialize("SRAM", *sram); ar.serialize("MB89352", *mb89352); ar.serialize("isWriteable", isWriteable); ar.serialize("mapped", mapped); } INSTANTIATE_SERIALIZE_METHODS(MegaSCSI); REGISTER_MSXDEVICE(MegaSCSI, "MegaSCSI"); } // namespace openmsx openmsx-0.10.0/src/ide/HDCommand.cc0000644000175000017500000000475512262345041017510 0ustar manuelmanuel00000000000000#include "HDCommand.hh" #include "HD.hh" #include "File.hh" #include "FileContext.hh" #include "FileException.hh" #include "CommandException.hh" #include "BooleanSetting.hh" #include "TclObject.hh" namespace openmsx { using std::string; using std::vector; // class HDCommand HDCommand::HDCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, HD& hd_, BooleanSetting& powerSetting_) : RecordedCommand(commandController, stateChangeDistributor, scheduler, hd_.getName()) , hd(hd_) , powerSetting(powerSetting_) { } void HDCommand::execute(const std::vector& tokens, TclObject& result, EmuTime::param /*time*/) { if (tokens.size() == 1) { result.addListElement(hd.getName() + ':'); result.addListElement(hd.getImageName().getResolved()); TclObject options(result.getInterpreter()); if (hd.isWriteProtected()) { options.addListElement("readonly"); } if (options.getListLength() != 0) { result.addListElement(options); } } else if ((tokens.size() == 2) || ((tokens.size() == 3) && tokens[1].getString() == "insert")) { if (powerSetting.getBoolean()) { throw CommandException( "Can only change hard disk image when MSX " "is powered down."); } int fileToken = 1; if (tokens[1].getString() == "insert") { if (tokens.size() > 2) { fileToken = 2; } else { throw CommandException( "Missing argument to insert subcommand"); } } try { Filename filename(tokens[fileToken].getString().str(), UserFileContext()); hd.switchImage(filename); // Note: the diskX command doesn't do this either, // so this has not been converted to TclObject style here // return filename; } catch (FileException& e) { throw CommandException("Can't change hard disk image: " + e.getMessage()); } } else { throw CommandException("Too many or wrong arguments."); } } string HDCommand::help(const vector& /*tokens*/) const { return hd.getName() + ": change the hard disk image for this hard disk drive\n"; } void HDCommand::tabCompletion(vector& tokens) const { vector extra; if (tokens.size() < 3) { extra.push_back("insert"); } completeFileName(tokens, UserFileContext(), extra); } bool HDCommand::needRecord(const vector& tokens) const { return tokens.size() > 1; } } // namespace openmsx openmsx-0.10.0/src/ide/HDCommand.hh0000644000175000017500000000161712262345041017514 0ustar manuelmanuel00000000000000#ifndef HDCOMMAND_HH #define HDCOMMAND_HH #include "RecordedCommand.hh" #include #include namespace openmsx { class CommandController; class StateChangeDistributor; class Scheduler; class TclObject; class HD; class BooleanSetting; class HDCommand : public RecordedCommand { public: HDCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, HD& hd, BooleanSetting& powerSetting); virtual void execute(const std::vector& tokens, TclObject& result, EmuTime::param time); virtual std::string help(const std::vector& tokens) const; virtual void tabCompletion(std::vector& tokens) const; virtual bool needRecord(const std::vector& tokens) const; private: HD& hd; const BooleanSetting& powerSetting; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/IDECDROM.cc0000644000175000017500000003030612262345041017073 0ustar manuelmanuel00000000000000#include "IDECDROM.hh" #include "DeviceConfig.hh" #include "MSXMotherBoard.hh" #include "File.hh" #include "FileContext.hh" #include "FileException.hh" #include "RecordedCommand.hh" #include "CommandException.hh" #include "TclObject.hh" #include "CliComm.hh" #include "endian.hh" #include "serialize.hh" #include "memory.hh" #include #include #include #include using std::string; using std::vector; namespace openmsx { class CDXCommand : public RecordedCommand { public: CDXCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, IDECDROM& cd); virtual void execute(const std::vector& tokens, TclObject& result, EmuTime::param time); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: IDECDROM& cd; }; static const unsigned MAX_CD = 26; typedef std::bitset CDInUse; IDECDROM::IDECDROM(const DeviceConfig& config) : AbstractIDEDevice(config.getMotherBoard()) , name("cdX") { auto& info = getMotherBoard().getSharedStuff("cdInUse"); if (info.counter == 0) { assert(!info.stuff); info.stuff = new CDInUse(); } ++info.counter; auto& cdInUse = *reinterpret_cast(info.stuff); unsigned id = 0; while (cdInUse[id]) { ++id; if (id == MAX_CD) { throw MSXException("Too many CDs"); } } name[2] = char('a' + id); cdInUse[id] = true; cdxCommand = make_unique( getMotherBoard().getCommandController(), getMotherBoard().getStateChangeDistributor(), getMotherBoard().getScheduler(), *this); senseKey = 0; remMedStatNotifEnabled = false; mediaChanged = false; getMotherBoard().getMSXCliComm().update(CliComm::HARDWARE, name, "add"); } IDECDROM::~IDECDROM() { auto& info = getMotherBoard().getSharedStuff("cdInUse"); assert(info.counter); assert(info.stuff); auto& cdInUse = *reinterpret_cast(info.stuff); getMotherBoard().getMSXCliComm().update(CliComm::HARDWARE, name, "remove"); unsigned id = name[2] - 'a'; assert(cdInUse[id]); cdInUse[id] = false; --info.counter; if (info.counter == 0) { assert(cdInUse.none()); delete &cdInUse; info.stuff = nullptr; } } bool IDECDROM::isPacketDevice() { return true; } const std::string& IDECDROM::getDeviceName() { static const std::string NAME = "OPENMSX CD-ROM"; return NAME; } void IDECDROM::fillIdentifyBlock(AlignedBuffer& buffer) { // 1... ....: removable media // .10. ....: fast handling of packet command (immediate, in fact) // .... .1..: incomplete response: // fields that depend on medium are undefined // .... ..00: support for 12-byte packets buffer[0 * 2 + 0] = 0xC4; // 10.. ....: ATAPI // ...0 0101: CD-ROM device buffer[0 * 2 + 1] = 0x85; // ...1 ....: Removable Media Status Notification feature set supported buffer[ 83 * 2 + 0] = 0x10; // ...1 ....: Removable Media Status Notification feature set enabled buffer[ 86 * 2 + 0] = remMedStatNotifEnabled * 0x10; // .... ..01: Removable Media Status Notification feature set supported (again??) buffer[127 * 2 + 0] = 0x01; } unsigned IDECDROM::readBlockStart(AlignedBuffer& buffer, unsigned count) { assert(readSectorData); if (file) { //fprintf(stderr, "read sector data at %08X\n", transferOffset); file->seek(transferOffset); file->read(buffer, count); transferOffset += count; return count; } else { //fprintf(stderr, "read sector failed: no medium\n"); // TODO: Check whether more error flags should be set. abortReadTransfer(0); return 0; } } void IDECDROM::readEnd() { setInterruptReason(I_O | C_D); } void IDECDROM::writeBlockComplete(AlignedBuffer& buffer, unsigned count) { // Currently, packet writes are the only kind of write transfer. assert(count == 12); (void)count; // avoid warning executePacketCommand(buffer); } void IDECDROM::executeCommand(byte cmd) { switch (cmd) { case 0xA0: // Packet Command (ATAPI) // Determine packet size for data packets. byteCountLimit = getByteCount(); //fprintf(stderr, "ATAPI Command, byte count limit %04X\n", // byteCountLimit); // Prepare to receive the command. startWriteTransfer(12); setInterruptReason(C_D); break; case 0xDA: // ATA Get Media Status if (remMedStatNotifEnabled) { setError(0); } else { // na WP MC na MCR ABRT NM obs byte err = 0; if (file) { err |= 0x40; // WP (write protected) } else { err |= 0x02; // NM (no media inserted) } // MCR (media change request) is not yet supported if (mediaChanged) { err |= 0x20; // MC (media changed) mediaChanged = false; } //fprintf(stderr, "Get Media status: %02X\n", err); setError(err); } break; case 0xEF: // Set Features switch (getFeatureReg()) { case 0x31: // Disable Media Status Notification. remMedStatNotifEnabled = false; break; case 0x95: // Enable Media Status Notification setLBAMid(0x00); // version // .... .0..: capable of physically ejecting media // .... ..0.: capable of locking the media // .... ...X: previous enabled state setLBAHigh(remMedStatNotifEnabled); remMedStatNotifEnabled = true; break; default: // other subcommands handled by base class AbstractIDEDevice::executeCommand(cmd); } break; default: // all others AbstractIDEDevice::executeCommand(cmd); } } void IDECDROM::startPacketReadTransfer(unsigned count) { // TODO: Recompute for each packet. // TODO: Take even/odd stuff into account. // Note: The spec says maximum byte count is 0xFFFE, but I prefer // powers of two, so I'll use 0x8000 instead (the device is // free to set limitations of its own). unsigned packetSize = 512; /*std::min( byteCountLimit, // limit from user std::min(sizeof(buffer), 0x8000u) // device and spec limit );*/ unsigned size = std::min(packetSize, count); setByteCount(size); setInterruptReason(I_O); } void IDECDROM::executePacketCommand(AlignedBuffer& packet) { // It seems that unlike ATA which uses words at the basic data unit, // ATAPI uses bytes. //fprintf(stderr, "ATAPI Packet:"); //for (unsigned i = 0; i < 12; i++) { // fprintf(stderr, " %02X", packet[i]); //} //fprintf(stderr, "\n"); readSectorData = false; switch (packet[0]) { case 0x03: { // REQUEST SENSE Command // TODO: Find out what the purpose of the allocation length is. // In practice, it seems to be 18, which is the amount we want // to return, but what if it would be different? //int allocationLength = packet[4]; //fprintf(stderr, " request sense: %d bytes\n", allocationLength); const int byteCount = 18; startPacketReadTransfer(byteCount); auto& buffer = startShortReadTransfer(byteCount); for (int i = 0; i < byteCount; i++) { buffer[i] = 0x00; } buffer[ 0] = 0xF0; buffer[ 2] = senseKey >> 16; // sense key buffer[12] = (senseKey >> 8) & 0xFF; // ASC buffer[13] = senseKey & 0xFF; // ASQ buffer[ 7] = byteCount - 7; senseKey = 0; break; } case 0x43: { // READ TOC/PMA/ATIP Command //bool msf = packet[1] & 2; int format = packet[2] & 0x0F; //int trackOrSession = packet[6]; //int allocLen = (packet[7] << 8) | packet[8]; //int control = packet[9]; switch (format) { case 0: { // TOC //fprintf(stderr, " read TOC: %s addressing, " // "start track %d, allocation length 0x%04X\n", // msf ? "MSF" : "logical block", // trackOrSession, allocLen); setError(ABORT); break; } case 1: // Session Info case 2: // Full TOC case 3: // PMA case 4: // ATIP default: fprintf(stderr, " read TOC: format %d not implemented\n", format); setError(ABORT); } break; } case 0xA8: { // READ Command int sectorNumber = Endian::read_UA_B32(&packet[2]); int sectorCount = Endian::read_UA_B32(&packet[6]); //fprintf(stderr, " read(12): sector %d, count %d\n", // sectorNumber, sectorCount); // There are three block sizes: // - byteCountLimit: set by the host // maximum block size for transfers // - byteCount: determined by the device // actual block size for transfers // - transferCount wrap: emulation thingy // transparent to host //fprintf(stderr, "byte count limit: %04X\n", byteCountLimit); //unsigned byteCount = sectorCount * 2048; //unsigned byteCount = sizeof(buffer); //unsigned byteCount = packetSize; /* if (byteCount > byteCountLimit) { byteCount = byteCountLimit; } if (byteCount > 0xFFFE) { byteCount = 0xFFFE; } */ //fprintf(stderr, "byte count: %04X\n", byteCount); readSectorData = true; transferOffset = sectorNumber * 2048; unsigned count = sectorCount * 2048; startPacketReadTransfer(count); startLongReadTransfer(count); break; } default: fprintf(stderr, " unknown packet command 0x%02X\n", packet[0]); setError(ABORT); } } void IDECDROM::eject() { file.reset(); mediaChanged = true; senseKey = 0x06 << 16; // unit attention (medium changed) getMotherBoard().getMSXCliComm().update(CliComm::MEDIA, name, ""); } void IDECDROM::insert(const string& filename) { file = make_unique(filename); mediaChanged = true; senseKey = 0x06 << 16; // unit attention (medium changed) getMotherBoard().getMSXCliComm().update(CliComm::MEDIA, name, filename); } // class CDXCommand CDXCommand::CDXCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, IDECDROM& cd_) : RecordedCommand(commandController, stateChangeDistributor, scheduler, cd_.name) , cd(cd_) { } void CDXCommand::execute(const std::vector& tokens, TclObject& result, EmuTime::param /*time*/) { if (tokens.size() == 1) { auto* file = cd.file.get(); result.addListElement(cd.name + ':'); result.addListElement(file ? file->getURL() : ""); if (!file) result.addListElement("empty"); } else if ((tokens.size() == 2) && ((tokens[1].getString() == "eject") || (tokens[1].getString() == "-eject"))) { cd.eject(); // TODO check for locked tray if (tokens[1].getString() == "-eject") { result.setString( "Warning: use of '-eject' is deprecated, " "instead use the 'eject' subcommand"); } } else if ((tokens.size() == 2) || ((tokens.size() == 3) && (tokens[1].getString() == "insert"))) { int fileToken = 1; if (tokens[1].getString() == "insert") { if (tokens.size() > 2) { fileToken = 2; } else { throw CommandException( "Missing argument to insert subcommand"); } } try { string filename = UserFileContext().resolve( tokens[fileToken].getString().str()); cd.insert(filename); // return filename; // Note: the diskX command doesn't do this either, so this has not been converted to TclObject style here } catch (FileException& e) { throw CommandException("Can't change cd image: " + e.getMessage()); } } else { throw CommandException("Too many or wrong arguments."); } } string CDXCommand::help(const vector& /*tokens*/) const { return cd.name + " : display the cd image for this CDROM drive\n" + cd.name + " eject : eject the cd image from this CDROM drive\n" + cd.name + " insert : change the cd image for this CDROM drive\n" + cd.name + " : change the cd image for this CDROM drive\n"; } void CDXCommand::tabCompletion(vector& tokens) const { static const char* const extra[] = { "eject", "insert" }; completeFileName(tokens, UserFileContext(), extra); } template void IDECDROM::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); string filename = file ? file->getURL() : ""; ar.serialize("filename", filename); if (ar.isLoader()) { // re-insert CDROM before restoring 'mediaChanged', 'senseKey' if (filename.empty()) { eject(); } else { insert(filename); } } ar.serialize("byteCountLimit", byteCountLimit); ar.serialize("transferOffset", transferOffset); ar.serialize("senseKey", senseKey); ar.serialize("readSectorData", readSectorData); ar.serialize("remMedStatNotifEnabled", remMedStatNotifEnabled); ar.serialize("mediaChanged", mediaChanged); } INSTANTIATE_SERIALIZE_METHODS(IDECDROM); REGISTER_POLYMORPHIC_INITIALIZER(IDEDevice, IDECDROM, "IDECDROM"); } // namespace openmsx openmsx-0.10.0/src/ide/IDEDeviceFactory.cc0000644000175000017500000000125412262345041020756 0ustar manuelmanuel00000000000000#include "IDEDeviceFactory.hh" #include "DummyIDEDevice.hh" #include "IDEHD.hh" #include "IDECDROM.hh" #include "DeviceConfig.hh" #include "MSXException.hh" #include "memory.hh" using std::unique_ptr; namespace openmsx { namespace IDEDeviceFactory { unique_ptr create(const DeviceConfig& config) { if (!config.getXML()) { return make_unique(); } const std::string& type = config.getChildData("type"); if (type == "IDEHD") { return make_unique(config); } else if (type == "IDECDROM") { return make_unique(config); } throw MSXException("Unknown IDE device: " + type); } } // namespace IDEDeviceFactory } // namespace openmsx openmsx-0.10.0/src/ide/GoudaSCSI.hh0000644000175000017500000000142312262345041017436 0ustar manuelmanuel00000000000000#ifndef GOUDASCSI_HH #define GOUDASCSI_HH #include "MSXDevice.hh" #include namespace openmsx { class WD33C93; class Rom; class GoudaSCSI : public MSXDevice { public: explicit GoudaSCSI(const DeviceConfig& config); virtual ~GoudaSCSI(); virtual void reset(EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; virtual byte readIO(word port, EmuTime::param time); virtual void writeIO(word port, byte value, EmuTime::param time); virtual byte peekIO(word port, EmuTime::param time) const; template void serialize(Archive& ar, unsigned version); private: const std::unique_ptr rom; const std::unique_ptr wd33c93; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/SCSILS120.cc0000644000175000017500000005452312262345041017137 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDevice.c,v ** Revision: 1.10 ** Date: 2007-05-21 21:38:29 +0200 (Mon, 21 May 2007) ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, white cat */ /* * Notes: * It follows the SCSI1(CCS) standard or the SCSI2 standard. * Only the direct access device is supported now. * Message system might be imperfect. * * NOTE: this version supports a removable LS-120 disk, as the class * name suggests. Refer to revision 6526 of SCSIHD.cc to see what was removed * from the generic/parameterised code. */ #include "SCSILS120.hh" #include "File.hh" #include "FileOperations.hh" #include "FileException.hh" #include "LedStatus.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" #include "DeviceConfig.hh" #include "RecordedCommand.hh" #include "CliComm.hh" #include "TclObject.hh" #include "CommandException.hh" #include "FileContext.hh" #include "endian.hh" #include "serialize.hh" #include "memory.hh" #include #include #include #include //#include //#undef PRT_DEBUG /* #define PRT_DEBUG(mes) \ do { \ std::cout << mes << std::endl; \ } while (0) */ using std::string; using std::vector; namespace openmsx { // Medium type (value like LS-120) static const byte MT_UNKNOWN = 0x00; static const byte MT_2DD_UN = 0x10; static const byte MT_2DD = 0x11; static const byte MT_2HD_UN = 0x20; static const byte MT_2HD_12_98 = 0x22; static const byte MT_2HD_12 = 0x23; static const byte MT_2HD_144 = 0x24; static const byte MT_LS120 = 0x31; static const byte MT_NO_DISK = 0x70; static const byte MT_DOOR_OPEN = 0x71; static const byte MT_FMT_ERROR = 0x72; static const byte inqdata[36] = { 0, // bit5-0 device type code. 0, // bit7 = 1 removable device 2, // bit7,6 ISO version. bit5,4,3 ECMA version. // bit2,1,0 ANSI Version (001=SCSI1, 010=SCSI2) 2, // bit7 AENC. bit6 TrmIOP. // bit3-0 Response Data Format. (0000=SCSI1, 0001=CCS, 0010=SCSI2) 51, // addtional length 0, 0,// reserved 0, // bit7 RelAdr, bit6 WBus32, bit5 Wbus16, bit4 Sync, bit3 Linked, // bit2 reseved bit1 CmdQue, bit0 SftRe 'o', 'p', 'e', 'n', 'M', 'S', 'X', ' ', // vendor ID (8bytes) 'S', 'C', 'S', 'I', '2', ' ', 'L', 'S', // product ID (16bytes) '-', '1', '2', '0', 'd', 'i', 's', 'k', '0', '1', '0', 'a' // product version (ASCII 4bytes) }; // for FDSFORM.COM static const char fds120[28 + 1] = "IODATA LS-120 COSM 0001"; static const unsigned BUFFER_BLOCK_SIZE = SCSIDevice::BUFFER_SIZE / SectorAccessibleDisk::SECTOR_SIZE; class LSXCommand : public RecordedCommand { public: LSXCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, SCSILS120& ls); virtual void execute(const std::vector& tokens, TclObject& result, EmuTime::param time); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; private: SCSILS120& ls; }; static const unsigned MAX_LS = 26; typedef std::bitset LSInUse; SCSILS120::SCSILS120(const DeviceConfig& targetconfig, AlignedBuffer& buf, unsigned mode_) : motherBoard(targetconfig.getMotherBoard()) , buffer(buf) , name("lsX") , mode(mode_) , scsiId(targetconfig.getAttributeAsInt("id")) { auto& info = motherBoard.getSharedStuff("lsInUse"); if (info.counter == 0) { assert(!info.stuff); info.stuff = new LSInUse(); } ++info.counter; auto& lsInUse = *reinterpret_cast(info.stuff); unsigned id = 0; while (lsInUse[id]) { ++id; if (id == MAX_LS) { throw MSXException("Too many LSs"); } } name[2] = char('a' + id); lsInUse[id] = true; lsxCommand = make_unique( motherBoard.getCommandController(), motherBoard.getStateChangeDistributor(), motherBoard.getScheduler(), *this); reset(); } SCSILS120::~SCSILS120() { PRT_DEBUG("ls120 close for ls120 " << int(scsiId)); auto& info = motherBoard.getSharedStuff("lsInUse"); assert(info.counter); assert(info.stuff); auto& lsInUse = *reinterpret_cast(info.stuff); motherBoard.getMSXCliComm().update(CliComm::HARDWARE, name, "remove"); unsigned id = name[2] - 'a'; assert(lsInUse[id]); lsInUse[id] = false; --info.counter; if (info.counter == 0) { assert(lsInUse.none()); delete &lsInUse; info.stuff = nullptr; } } void SCSILS120::reset() { mediaChanged = false; currentSector = 0; currentLength = 0; busReset(); } void SCSILS120::busReset() { PRT_DEBUG("SCSI: bus reset on " << int(scsiId)); keycode = 0; unitAttention = (mode & MODE_UNITATTENTION) != 0; } void SCSILS120::disconnect() { motherBoard.getLedStatus().setLed(LedStatus::FDD, false); } // Check the initiator in the call origin. bool SCSILS120::isSelected() { lun = 0; return file != nullptr; } bool SCSILS120::getReady() { if (file) return true; keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT; return false; } void SCSILS120::testUnitReady() { if ((mode & MODE_NOVAXIS) == 0) { if (getReady() && mediaChanged && (mode & MODE_MEGASCSI)) { // Disk change is surely sent for the driver of MEGA-SCSI. keycode = SCSI::SENSE_POWER_ON; } } mediaChanged = false; } void SCSILS120::startStopUnit() { switch (cdb[4]) { case 2: // Eject eject(); PRT_DEBUG("eject disk " << int(scsiId)); break; case 3: // Insert TODO: how can this happen? //if (!diskPresent(diskId)) { // *disk = disk; // updateExtendedDiskName(diskId, disk->fileName, disk->fileNameInZip); // boardChangeDiskette(diskId, disk->fileName, disk->fileNameInZip); PRT_DEBUG("insert ls120 " << int(scsiId)); //} break; } } unsigned SCSILS120::inquiry() { auto total = getNbSectors(); unsigned length = currentLength; bool fdsmode = (total > 0) && (total <= 2880); if (length == 0) return 0; if (fdsmode) { memcpy(buffer + 2, inqdata + 2, 6); memcpy(buffer + 8, fds120, 28); } else { memcpy(buffer + 2, inqdata + 2, 34); } buffer[0] = SCSI::DT_DirectAccess; buffer[1] = 0x80; // removable if (!(mode & BIT_SCSI2)) { buffer[2] = 1; buffer[3] = 1; if (!fdsmode) buffer[20] = '1'; } else { if (mode & BIT_SCSI3) { buffer[2] = 5; if (!fdsmode) buffer[20] = '3'; } } if (mode & BIT_SCSI3) { length = std::min(length, 96u); buffer[4] = 91; if (length > 56) { memset(buffer + 56, 0, 40); buffer[58] = 0x03; buffer[60] = 0x01; buffer[61] = 0x80; } } else { length = std::min(length, 56u); } if (length > 36) { string filename = FileOperations::getFilename(file->getURL()).str(); filename.resize(20, ' '); memcpy(buffer + 36, filename.data(), 20); } return length; } unsigned SCSILS120::modeSense() { byte* pBuffer = buffer; if ((currentLength > 0) && (cdb[2] == 3)) { auto total = getNbSectors(); byte media = MT_UNKNOWN; byte sectors = 64; byte blockLength = SECTOR_SIZE >> 8; byte tracks = 8; byte size = 4 + 24; byte removable = 0xa0; memset(pBuffer + 2, 0, 34); if (total == 0) { media = MT_NO_DISK; } else { if (total == 1440) { media = MT_2DD; sectors = 9; blockLength = 2048 >> 8; // FDS-120 value tracks = 160; } else { if (total == 2880) { media = MT_2HD_144; sectors = 18; blockLength = 2048 >> 8; tracks = 160; } } } // Mode Parameter Header 4bytes pBuffer[1] = media; // Medium Type pBuffer[3] = 8; // block descripter length pBuffer += 4; // Disable Block Descriptor check if (!(cdb[1] & 0x08)) { // Block Descriptor 8bytes pBuffer[1] = (total >> 16) & 0xff; // 1..3 Number of Blocks pBuffer[2] = (total >> 8) & 0xff; pBuffer[3] = (total >> 0) & 0xff; pBuffer[6] = blockLength & 0xff; // 5..7 Block Length in Bytes pBuffer += 8; size += 8; } // Format Device Page 24bytes pBuffer[ 0] = 3; // 0 Page pBuffer[ 1] = 0x16; // 1 Page Length pBuffer[ 3] = tracks; // 2, 3 Tracks per Zone pBuffer[11] = sectors; // 10,11 Sectors per Track pBuffer[12] = blockLength; // 12,13 Data Bytes per Physical Sector pBuffer[20] = removable; // 20 bit7 Soft Sector bit5 Removable buffer[0] = size - 1; // sense data length return std::min(currentLength, size); } keycode = SCSI::SENSE_INVALID_COMMAND_CODE; return 0; } unsigned SCSILS120::requestSense() { unsigned length = currentLength; unsigned tmpKeycode = unitAttention ? SCSI::SENSE_POWER_ON : keycode; unitAttention = false; PRT_DEBUG("Request Sense: keycode = " << tmpKeycode); keycode = SCSI::SENSE_NO_SENSE; memset(buffer + 1, 0, 17); if (length == 0) { if (mode & BIT_SCSI2) { return 0; } buffer[ 0] = (tmpKeycode >> 8) & 0xff; // Sense code length = 4; } else { buffer[ 0] = 0x70; buffer[ 2] = (tmpKeycode >> 16) & 0xff; // Sense key buffer[ 7] = 10; // Additional sense length buffer[12] = (tmpKeycode >> 8) & 0xff; // Additional sense code buffer[13] = (tmpKeycode >> 0) & 0xff; // Additional sense code qualifier length = std::min(length, 18u); } return length; } bool SCSILS120::checkReadOnly() { if (file->isReadOnly()) { keycode = SCSI::SENSE_WRITE_PROTECT; return true; } return false; } unsigned SCSILS120::readCapacity() { unsigned block = unsigned(getNbSectors()); if (block == 0) { keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT; PRT_DEBUG("ls120 " << int(scsiId) << ": drive not ready"); return 0; } PRT_DEBUG("total block: " << block); --block; Endian::writeB32(&buffer[0], block); Endian::writeB32(&buffer[4], SECTOR_SIZE); // TODO see SCSIHD return 8; } bool SCSILS120::checkAddress() { unsigned total = unsigned(getNbSectors()); if (total == 0) { keycode = SCSI::SENSE_MEDIUM_NOT_PRESENT; PRT_DEBUG("ls120 " << int(scsiId) << ": drive not ready"); return false; } if ((currentLength > 0) && (currentSector + currentLength <= total)) { return true; } PRT_DEBUG("ls120 " << int(scsiId) << ": IllegalBlockAddress"); keycode = SCSI::SENSE_ILLEGAL_BLOCK_ADDRESS; return false; } // Execute scsiDeviceCheckAddress previously. unsigned SCSILS120::readSector(unsigned& blocks) { motherBoard.getLedStatus().setLed(LedStatus::FDD, true); unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE); unsigned counter = currentLength * SECTOR_SIZE; PRT_DEBUG("ls120#" << int(scsiId) << " read sector: " << currentSector << ' ' << numSectors); try { // TODO: somehow map this to SectorAccessibleDisk::readSector? file->seek(SECTOR_SIZE * currentSector); file->read(buffer, SECTOR_SIZE * numSectors); currentSector += numSectors; currentLength -= numSectors; blocks = currentLength; return counter; } catch (FileException&) { blocks = 0; keycode = SCSI::SENSE_UNRECOVERED_READ_ERROR; return 0; } } unsigned SCSILS120::dataIn(unsigned& blocks) { if (cdb[0] == SCSI::OP_READ10) { unsigned counter = readSector(blocks); if (counter) { return counter; } } PRT_DEBUG("dataIn error " << int(cdb[0])); blocks = 0; return 0; } // Execute scsiDeviceCheckAddress and scsiDeviceCheckReadOnly previously. unsigned SCSILS120::writeSector(unsigned& blocks) { motherBoard.getLedStatus().setLed(LedStatus::FDD, true); unsigned numSectors = std::min(currentLength, BUFFER_BLOCK_SIZE); PRT_DEBUG("ls120#" << int(scsiId) << " write sector: " << currentSector << ' ' << numSectors); // TODO: somehow map this to SectorAccessibleDisk::writeSector? try { file->seek(SECTOR_SIZE * currentSector); file->write(buffer, SECTOR_SIZE * numSectors); currentSector += numSectors; currentLength -= numSectors; unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE); blocks = currentLength - tmp; unsigned counter = tmp * SECTOR_SIZE; return counter; } catch (FileException&) { keycode = SCSI::SENSE_WRITE_FAULT; blocks = 0; return 0; } } unsigned SCSILS120::dataOut(unsigned& blocks) { if (cdb[0] == SCSI::OP_WRITE10) { return writeSector(blocks); } PRT_DEBUG("dataOut error " << int(cdb[0])); blocks = 0; return 0; } // MBR erase only void SCSILS120::formatUnit() { if (getReady() && !checkReadOnly()) { memset(buffer, 0, SECTOR_SIZE); try { file->seek(0); file->write(buffer, SECTOR_SIZE); unitAttention = true; mediaChanged = true; } catch (FileException&) { keycode = SCSI::SENSE_WRITE_FAULT; } } } byte SCSILS120::getStatusCode() { byte result = keycode ? SCSI::ST_CHECK_CONDITION : SCSI::ST_GOOD; PRT_DEBUG("SCSI status code: \n" << int(result)); return result; } void SCSILS120::eject() { file.reset(); mediaChanged = true; if (mode & MODE_UNITATTENTION) { unitAttention = true; } motherBoard.getMSXCliComm().update(CliComm::MEDIA, name, ""); } void SCSILS120::insert(const string& filename) { file = make_unique(filename); file->setFilePool(motherBoard.getReactor().getFilePool()); mediaChanged = true; if (mode & MODE_UNITATTENTION) { unitAttention = true; } motherBoard.getMSXCliComm().update(CliComm::MEDIA, name, filename); } unsigned SCSILS120::executeCmd(const byte* cdb_, SCSI::Phase& phase, unsigned& blocks) { PRT_DEBUG("SCSI Command: " << int(cdb[0])); memcpy(cdb, cdb_, sizeof(cdb)); message = 0; phase = SCSI::STATUS; blocks = 0; // check unit attention if (unitAttention && (mode & MODE_UNITATTENTION) && (cdb[0] != SCSI::OP_INQUIRY) && (cdb[0] != SCSI::OP_REQUEST_SENSE)) { unitAttention = false; keycode = SCSI::SENSE_POWER_ON; if (cdb[0] == SCSI::OP_TEST_UNIT_READY) { mediaChanged = false; } PRT_DEBUG("Unit Attention. This command is not executed."); return 0; } // check LUN if (((cdb[1] & 0xe0) || lun) && (cdb[0] != SCSI::OP_REQUEST_SENSE) && !(cdb[0] == SCSI::OP_INQUIRY && !(mode & MODE_NOVAXIS))) { keycode = SCSI::SENSE_INVALID_LUN; PRT_DEBUG("check LUN error"); return 0; } if (cdb[0] != SCSI::OP_REQUEST_SENSE) { keycode = SCSI::SENSE_NO_SENSE; } if (cdb[0] < SCSI::OP_GROUP1) { currentSector = ((cdb[1] & 0x1f) << 16) | (cdb[2] << 8) | cdb[3]; currentLength = cdb[4]; switch (cdb[0]) { case SCSI::OP_TEST_UNIT_READY: PRT_DEBUG("TestUnitReady"); testUnitReady(); return 0; case SCSI::OP_INQUIRY: { PRT_DEBUG("Inquiry " << currentLength); unsigned counter = inquiry(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_REQUEST_SENSE: { PRT_DEBUG("RequestSense"); unsigned counter = requestSense(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_READ6: PRT_DEBUG("Read6: " << currentSector << ' ' << currentLength); if (currentLength == 0) { currentLength = SECTOR_SIZE >> 1; } if (checkAddress()) { unsigned counter = readSector(blocks); if (counter) { cdb[0] = SCSI::OP_READ10; phase = SCSI::DATA_IN; return counter; } } return 0; case SCSI::OP_WRITE6: PRT_DEBUG("Write6: " << currentSector << ' ' << currentLength); if (currentLength == 0) { currentLength = SECTOR_SIZE >> 1; } if (checkAddress() && !checkReadOnly()) { motherBoard.getLedStatus().setLed(LedStatus::FDD, true); unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE); blocks = currentLength - tmp; unsigned counter = tmp * SECTOR_SIZE; cdb[0] = SCSI::OP_WRITE10; phase = SCSI::DATA_OUT; return counter; } return 0; case SCSI::OP_SEEK6: PRT_DEBUG("Seek6: " << currentSector); motherBoard.getLedStatus().setLed(LedStatus::FDD, true); currentLength = 1; checkAddress(); return 0; case SCSI::OP_MODE_SENSE: { PRT_DEBUG("ModeSense: " << currentLength); unsigned counter = modeSense(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_FORMAT_UNIT: PRT_DEBUG("FormatUnit"); formatUnit(); return 0; case SCSI::OP_START_STOP_UNIT: PRT_DEBUG("StartStopUnit"); startStopUnit(); return 0; case SCSI::OP_REZERO_UNIT: case SCSI::OP_REASSIGN_BLOCKS: case SCSI::OP_RESERVE_UNIT: case SCSI::OP_RELEASE_UNIT: case SCSI::OP_SEND_DIAGNOSTIC: PRT_DEBUG("SCSI_Group0 dummy"); return 0; } } else { currentSector = Endian::read_UA_B32(&cdb[2]); currentLength = Endian::read_UA_B16(&cdb[7]); switch (cdb[0]) { case SCSI::OP_READ10: PRT_DEBUG("Read10: " << currentSector << ' ' << currentLength); if (checkAddress()) { unsigned counter = readSector(blocks); if (counter) { phase = SCSI::DATA_IN; return counter; } } return 0; case SCSI::OP_WRITE10: PRT_DEBUG("Write10: " << currentSector << ' ' << currentLength); if (checkAddress() && !checkReadOnly()) { unsigned tmp = std::min(currentLength, BUFFER_BLOCK_SIZE); blocks = currentLength - tmp; unsigned counter = tmp * SECTOR_SIZE; phase = SCSI::DATA_OUT; return counter; } return 0; case SCSI::OP_READ_CAPACITY: { PRT_DEBUG("ReadCapacity"); unsigned counter = readCapacity(); if (counter) { phase = SCSI::DATA_IN; } return counter; } case SCSI::OP_SEEK10: PRT_DEBUG("Seek10: " << currentSector); motherBoard.getLedStatus().setLed(LedStatus::FDD, true); currentLength = 1; checkAddress(); return 0; } } PRT_DEBUG("unsupported command " << cdb[0]); keycode = SCSI::SENSE_INVALID_COMMAND_CODE; return 0; } unsigned SCSILS120::executingCmd(SCSI::Phase& phase, unsigned& blocks) { phase = SCSI::EXECUTE; blocks = 0; return 0; // we're very fast } byte SCSILS120::msgIn() { byte result = message; message = 0; //PRT_DEBUG("SCSIDevice " << scsiId << " msgIn returning " << result); return result; } /* scsiDeviceMsgOut() Notes: [out] -1: Busfree demand. (Please process it in the call origin.) bit2: Status phase demand. Error happend. bit1: Make it to a busfree if ATN has not been released. bit0: There is a message(MsgIn). */ int SCSILS120::msgOut(byte value) { PRT_DEBUG("SCSI #" << int(scsiId) << " message out: " << int(value)); if (value & 0x80) { lun = value & 7; return 0; } switch (value) { case SCSI::MSG_INITIATOR_DETECT_ERROR: keycode = SCSI::SENSE_INITIATOR_DETECTED_ERR; return 6; case SCSI::MSG_BUS_DEVICE_RESET: busReset(); // fall-through case SCSI::MSG_ABORT: return -1; case SCSI::MSG_REJECT: case SCSI::MSG_PARITY_ERROR: case SCSI::MSG_NO_OPERATION: return 2; } message = SCSI::MSG_REJECT; return ((value >= 0x04) && (value <= 0x11)) ? 3 : 1; } size_t SCSILS120::getNbSectorsImpl() const { return file ? (file->getSize() / SECTOR_SIZE) : 0; } bool SCSILS120::isWriteProtectedImpl() const { return false; } Sha1Sum SCSILS120::getSha1Sum() { if (hasPatches()) { return SectorAccessibleDisk::getSha1Sum(); } return file->getSha1Sum(); } void SCSILS120::readSectorImpl(size_t sector, SectorBuffer& buf) { file->seek(sizeof(buf) * sector); file->read(&buf, sizeof(buf)); } void SCSILS120::writeSectorImpl(size_t sector, const SectorBuffer& buf) { file->seek(sizeof(buf) * sector); file->write(&buf, sizeof(buf)); } SectorAccessibleDisk* SCSILS120::getSectorAccessibleDisk() { return this; } const std::string& SCSILS120::getContainerName() const { return name; } bool SCSILS120::diskChanged() { return mediaChanged; // TODO not reset on read } int SCSILS120::insertDisk(const std::string& filename) { try { insert(filename); return 0; } catch (MSXException&) { return -1; } } // class LSXCommand LSXCommand::LSXCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, SCSILS120& ls_) : RecordedCommand(commandController, stateChangeDistributor, scheduler, ls_.name) , ls(ls_) { } void LSXCommand::execute(const std::vector& tokens, TclObject& result, EmuTime::param /*time*/) { if (tokens.size() == 1) { auto* file = ls.file.get(); result.addListElement(ls.name + ':'); result.addListElement(file ? file->getURL() : ""); if (!file) result.addListElement("empty"); } else if ((tokens.size() == 2) && ((tokens[1].getString() == "eject") || (tokens[1].getString() == "-eject"))) { ls.eject(); // TODO check for locked tray if (tokens[1].getString() == "-eject") { result.setString( "Warning: use of '-eject' is deprecated, " "instead use the 'eject' subcommand"); } } else if ((tokens.size() == 2) || ((tokens.size() == 3) && (tokens[1].getString() == "insert"))) { int fileToken = 1; if (tokens[1].getString() == "insert") { if (tokens.size() > 2) { fileToken = 2; } else { throw CommandException( "Missing argument to insert subcommand"); } } try { string filename = UserFileContext().resolve( tokens[fileToken].getString().str()); ls.insert(filename); // return filename; // Note: the diskX command doesn't do this either, so this has not been converted to TclObject style here } catch (FileException& e) { throw CommandException("Can't change disk image: " + e.getMessage()); } } else { throw CommandException("Too many or wrong arguments."); } } string LSXCommand::help(const vector& /*tokens*/) const { return ls.name + " : display the disk image for this LS-120 drive\n" + ls.name + " eject : eject the disk image from this LS-120 drive\n" + ls.name + " insert : change the disk image for this LS-120 drive\n" + ls.name + " : change the disk image for this LS-120 drive\n"; } void LSXCommand::tabCompletion(vector& tokens) const { static const char* const extra[] = { "eject", "insert" }; completeFileName(tokens, UserFileContext(), extra); } template void SCSILS120::serialize(Archive& ar, unsigned /*version*/) { string filename = file ? file->getURL() : ""; ar.serialize("filename", filename); if (ar.isLoader()) { // re-insert disk before restoring 'mediaChanged' if (filename.empty()) { eject(); } else { insert(filename); } } ar.serialize("keycode", keycode); ar.serialize("currentSector", currentSector); ar.serialize("currentLength", currentLength); ar.serialize("unitAttention", unitAttention); ar.serialize("mediaChanged", mediaChanged); ar.serialize("message", message); ar.serialize("lun", lun); ar.serialize_blob("cdb", cdb, sizeof(cdb)); } INSTANTIATE_SERIALIZE_METHODS(SCSILS120); REGISTER_POLYMORPHIC_INITIALIZER(SCSIDevice, SCSILS120, "SCSILS120"); } // namespace openmsx openmsx-0.10.0/src/ide/WD33C93.hh0000644000175000017500000000210512262345041016652 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/wd33c93.h,v ** Revision: 1.6 ** Date: 2007/03/22 10:55:08 ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, Ricardo Bittencourt, white cat */ #ifndef WD33C93_HH #define WD33C93_HH #include "SCSI.hh" #include "SCSIDevice.hh" #include "AlignedBuffer.hh" #include namespace openmsx { class DeviceConfig; class WD33C93 { public: explicit WD33C93(const DeviceConfig& config); ~WD33C93(); void reset(bool scsireset); byte readAuxStatus(); byte readCtrl(); byte peekAuxStatus() const; byte peekCtrl() const; void writeAdr(byte value); void writeCtrl(byte value); template void serialize(Archive& ar, unsigned version); private: void disconnect(); void execCmd(byte value); AlignedByteArray buffer; std::unique_ptr dev[8]; unsigned bufIdx; int counter; unsigned blockCounter; int tc; SCSI::Phase phase; byte myId; byte targetId; byte regs[32]; byte latch; bool devBusy; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/AbstractIDEDevice.hh0000644000175000017500000001663012262345041021130 0ustar manuelmanuel00000000000000#ifndef ABSTRACTIDEDEVICE_HH #define ABSTRACTIDEDEVICE_HH #include "IDEDevice.hh" #include "AlignedBuffer.hh" #include "serialize_meta.hh" #include namespace openmsx { class MSXMotherBoard; class AbstractIDEDevice : public IDEDevice { public: virtual void reset(EmuTime::param time); virtual word readData(EmuTime::param time); virtual byte readReg(nibble reg, EmuTime::param time); virtual void writeData(word value, EmuTime::param time); virtual void writeReg(nibble reg, byte value, EmuTime::param time); template void serialize(Archive& ar, unsigned version); protected: // Bit flags for the status register: static const byte DRDY = 0x40; static const byte DSC = 0x10; static const byte DRQ = 0x08; static const byte ERR = 0x01; // Bit flags for the error register: static const byte UNC = 0x40; static const byte IDNF = 0x10; static const byte ABORT = 0x04; explicit AbstractIDEDevice(MSXMotherBoard& motherBoard); virtual ~AbstractIDEDevice(); /** Is this device a packet (ATAPI) device? * @return True iff this device supports the packet commands. */ virtual bool isPacketDevice() = 0; /** Gets the device name to insert as "model number" into the identify * block. * @return An ASCII string, up to 40 characters long. */ virtual const std::string& getDeviceName() = 0; /** Tells a subclass to fill the device specific parts of the identify * block located in the buffer. * The generic part is already written there. * @param buffer Array of 512 bytes. */ virtual void fillIdentifyBlock(AlignedBuffer& buffer) = 0; /** Called when a block of read data should be buffered by the controller: * when the buffer is empty or at the start of the transfer. * @param buffer Pointer to the start of a byte array. * @param count Number of bytes to be filled by this method. * This number will not exceed the array size nor the transfer length. * @return The number of bytes that was added to the array, * or 0 if the transfer was aborted (the implementation of this method * must set the relevant error flags as well). */ virtual unsigned readBlockStart(AlignedBuffer& buffer, unsigned count) = 0; /** Called when a read transfer completes. * The default implementation does nothing. */ virtual void readEnd(); /** Called when a block of written data has been buffered by the controller: * when the buffer is full or at the end of the transfer. * @param buffer Pointer to the start of a byte array. * @param count Number of data bytes in the array. */ virtual void writeBlockComplete(AlignedBuffer& buffer, unsigned count) = 0; /** Starts execution of an IDE command. * Override this to implement additional commands and make sure you call * the superclass implementation for all commands that you don't handle. */ virtual void executeCommand(byte cmd); /** Indicates an error: sets error register, error flag, aborts transfers. * @param error Value to be written to the error register. */ void setError(byte error); /** Creates an LBA sector address from the contents of the sectorNumReg, * cylinderLowReg, cylinderHighReg and devHeadReg registers. */ unsigned getSectorNumber() const; /** Gets the number of sectors indicated by the sector count register. */ unsigned getNumSectors() const; /** Writes the interrupt reason register. * This is the same as register as sector count, but serves a different * purpose. */ void setInterruptReason(byte value); /** Reads the byte count limit of a packet transfer in the registers. * The cylinder low/high registers are used for this. */ unsigned getByteCount(); /** Writes the byte count of a packet transfer in the registers. * The cylinder low/high registers are used for this. */ void setByteCount(unsigned count); /** Writes a 28-bit LBA sector number in the registers. * The cylinder low/high registers are used for this. */ void setSectorNumber(unsigned lba); /** Indicates the start of a read data transfer which uses blocks. * The readBlockStart() method is called at the start of each block. * The first block will be read immediately, so make sure you initialise * all variables needed by readBlockStart() before calling this method. * @param count Total number of bytes to transfer. */ void startLongReadTransfer(unsigned count); /** Indicates the start of a read data transfer where all data fits * into the buffer at once. * @param count Total number of bytes to transfer. * @return Pointer to the start of the buffer. * The caller should write the data there. * The relevant part of the buffer contains zeroes. */ AlignedBuffer& startShortReadTransfer(unsigned count); /** Aborts the read transfer in progress. */ void abortReadTransfer(byte error); /** Indicates the start of a write data transfer. * @param count Total number of bytes to transfer. */ void startWriteTransfer(unsigned count); /** Aborts the write transfer in progress. */ void abortWriteTransfer(byte error); byte getFeatureReg() const { return featureReg; } void setLBALow (byte value) { sectorNumReg = value; } void setLBAMid (byte value) { cylinderLowReg = value; } void setLBAHigh(byte value) { cylinderHighReg = value; } MSXMotherBoard& getMotherBoard() const { return motherBoard; } private: /** Perform diagnostic and return result. * Actually, just return success, because we don't emulate faulty hardware. */ byte diagnostic(); /** Puts special values in the sector address, sector count and device * registers to identify the type of device. * @param preserveDevice If true, preserve the value of the DEV bit; * if false, set the DEV bit to 0. */ void createSignature(bool preserveDevice = false); /** Puts the output for the IDENTIFY DEVICE command in the buffer. * @param buffer Pointer to the start of the buffer. * The buffer must be at least 512 bytes in size. */ void createIdentifyBlock(AlignedBuffer& buffer); /** Initialises registers for a data transfer. */ void startReadTransfer(); /** Initialises buffer related variables for the next data block. * Calls readBlockStart() to deliver the actual data. */ void readNextBlock(); /** Indicates that a read transfer starts. */ void setTransferRead(bool status); /** Initialises buffer related variables for the next data block. * Make sure transferCount is initialised before calling this method. */ void writeNextBlock(); /** Indicates that a write transfer starts. */ void setTransferWrite(bool status); MSXMotherBoard& motherBoard; /** Data buffer shared by all transfers. * The size must be a multiple of 512. * Right now I don't see any reason to make it larger than the minimum * size of 1 * 512. */ AlignedByteArray<512> buffer; /** Index of current read/write position in the buffer. */ unsigned transferIdx; /** Number of bytes remaining in the buffer. */ unsigned bufferLeft; /** Number of bytes remaining in the transfer after this buffer. * (total bytes remaining == transferCount + bufferLeft) */ unsigned transferCount; // ATA registers: byte errorReg; byte sectorCountReg; byte sectorNumReg; byte cylinderLowReg; byte cylinderHighReg; byte devHeadReg; byte statusReg; byte featureReg; bool transferRead; bool transferWrite; }; REGISTER_BASE_NAME_HELPER(AbstractIDEDevice, "IDEDevice"); } // namespace openmsx #endif // ABSTRACTIDEDEVICE_HH openmsx-0.10.0/src/ide/HD.cc0000644000175000017500000001553712262345041016211 0ustar manuelmanuel00000000000000#include "HD.hh" #include "File.hh" #include "FileContext.hh" #include "FileException.hh" #include "DeviceConfig.hh" #include "CliComm.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" #include "GlobalSettings.hh" #include "MSXException.hh" #include "HDCommand.hh" #include "serialize.hh" #include "memory.hh" #include "xrange.hh" #include #include namespace openmsx { using std::string; using std::vector; static const unsigned MAX_HD = 26; typedef std::bitset HDInUse; HD::HD(const DeviceConfig& config) : motherBoard(config.getMotherBoard()) , name("hdX") { auto& info = motherBoard.getSharedStuff("hdInUse"); if (info.counter == 0) { assert(!info.stuff); info.stuff = new HDInUse(); } ++info.counter; auto& hdInUse = *reinterpret_cast(info.stuff); unsigned id = 0; while (hdInUse[id]) { ++id; if (id == MAX_HD) { throw MSXException("Too many HDs"); } } // for exception safety, set hdInUse only at the end name[2] = char('a' + id); // For the initial hd image, savestate should only try exactly this // (resolved) filename. For user-specified hd images (commandline or // via hda command) savestate will try to re-resolve the filename. string original = config.getChildData("filename"); string resolved = config.getFileContext().resolveCreate(original); filename = Filename(resolved); try { file = make_unique(filename); filesize = file->getSize(); file->setFilePool(motherBoard.getReactor().getFilePool()); tigerTree = make_unique(*this, filesize); } catch (FileException&) { // Image didn't exist yet, but postpone image creation: // we don't want to create images during 'testconfig' filesize = size_t(config.getChildDataAsInt("size")) * 1024 * 1024; } alreadyTried = false; hdInUse[id] = true; hdCommand = make_unique( motherBoard.getCommandController(), motherBoard.getStateChangeDistributor(), motherBoard.getScheduler(), *this, motherBoard.getReactor().getGlobalSettings().getPowerSetting()); motherBoard.getMSXCliComm().update(CliComm::HARDWARE, name, "add"); } HD::~HD() { auto& info = motherBoard.getSharedStuff("hdInUse"); assert(info.counter); assert(info.stuff); auto& hdInUse = *reinterpret_cast(info.stuff); motherBoard.getMSXCliComm().update(CliComm::HARDWARE, name, "remove"); unsigned id = name[2] - 'a'; assert(hdInUse[id]); hdInUse[id] = false; --info.counter; if (info.counter == 0) { assert(hdInUse.none()); delete &hdInUse; info.stuff = nullptr; } } const string& HD::getName() const { return name; } const Filename& HD::getImageName() const { return filename; } void HD::openImage() { if (file) return; // image didn't exist yet, create new if (alreadyTried) { throw FileException("No HD image"); } alreadyTried = true; try { file = make_unique(filename, File::CREATE); file->truncate(filesize); file->setFilePool(motherBoard.getReactor().getFilePool()); tigerTree = make_unique(*this, filesize); } catch (FileException& e) { motherBoard.getMSXCliComm().printWarning( "Couldn't create HD image: " + e.getMessage()); throw; } } void HD::switchImage(const Filename& name) { file = make_unique(name); filename = name; filesize = file->getSize(); file->setFilePool(motherBoard.getReactor().getFilePool()); tigerTree = make_unique(*this, filesize); motherBoard.getMSXCliComm().update(CliComm::MEDIA, getName(), filename.getResolved()); } size_t HD::getNbSectorsImpl() const { const_cast(*this).openImage(); return filesize / sizeof(SectorBuffer); } void HD::readSectorImpl(size_t sector, SectorBuffer& buf) { openImage(); file->seek(sector * sizeof(buf)); file->read(&buf, sizeof(buf)); } void HD::writeSectorImpl(size_t sector, const SectorBuffer& buf) { openImage(); file->seek(sector * sizeof(buf)); file->write(&buf, sizeof(buf)); tigerTree->notifyChange(sector * sizeof(buf), sizeof(buf)); } bool HD::isWriteProtectedImpl() const { const_cast(*this).openImage(); return file->isReadOnly(); } Sha1Sum HD::getSha1Sum() { openImage(); if (hasPatches()) { return SectorAccessibleDisk::getSha1Sum(); } return file->getSha1Sum(); } std::string HD::getTigerTreeHash() { openImage(); return tigerTree->calcHash().toString(); // calls HD::getData() } uint8_t* HD::getData(size_t offset, size_t size) { assert(size <= 1024); assert((offset % sizeof(SectorBuffer)) == 0); assert((size % sizeof(SectorBuffer)) == 0); struct Work { char extra; // at least one byte before 'bufs' // likely here are padding bytes inbetween SectorBuffer bufs[1024 / sizeof(SectorBuffer)]; }; static Work work; // not reentrant size_t sector = offset / sizeof(SectorBuffer); for (auto i : xrange(size / sizeof(SectorBuffer))) { // This possibly applies IPS patches. readSector(sector++, work.bufs[i]); } return work.bufs[0].raw; } SectorAccessibleDisk* HD::getSectorAccessibleDisk() { return this; } const std::string& HD::getContainerName() const { return getName(); } bool HD::diskChanged() { return false; // TODO not implemented } int HD::insertDisk(const std::string& filename) { try { switchImage(Filename(filename)); return 0; } catch (MSXException&) { return -1; } } // version 1: initial version // version 2: replaced 'checksum'(=sha1) with 'tthsum` template void HD::serialize(Archive& ar, unsigned version) { Filename tmp = file ? filename : Filename(); ar.serialize("filename", tmp); if (ar.isLoader()) { if (tmp.empty()) { // lazily open file specified in config } else { tmp.updateAfterLoadState(); switchImage(tmp); assert(file); } } // store/check checksum if (file) { bool mismatch = false; if (ar.versionAtLeast(version, 2)) { // use tiger-tree-hash string oldTiger = ar.isLoader() ? "" : getTigerTreeHash(); ar.serialize("tthsum", oldTiger); if (ar.isLoader()) { string newTiger = getTigerTreeHash(); mismatch = oldTiger != newTiger; } } else { // use sha1 Sha1Sum oldChecksum; if (!ar.isLoader()) { oldChecksum = getSha1Sum(); } string oldChecksumStr = oldChecksum.empty() ? "" : oldChecksum.toString(); ar.serialize("checksum", oldChecksumStr); oldChecksum = oldChecksumStr.empty() ? Sha1Sum() : Sha1Sum(oldChecksumStr); if (ar.isLoader()) { Sha1Sum newChecksum = getSha1Sum(); mismatch = oldChecksum != newChecksum; } } if (ar.isLoader() && mismatch) { motherBoard.getMSXCliComm().printWarning( "The content of the harddisk " + tmp.getResolved() + " has changed since the time this savestate was " "created. This might result in emulation problems " "or even diskcorruption. To prevent the latter, " "the harddisk is now write-protected."); forceWriteProtect(); } } } INSTANTIATE_SERIALIZE_METHODS(HD); } // namespace openmsx openmsx-0.10.0/src/ide/SCSI.hh0000644000175000017500000001026412262345041016461 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/ScsiDefs.h,v ** Revision: 1.2 ** Date: 2007/03/24 08:01:48 ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, white cat */ #ifndef SCSI_HH #define SCSI_HH #include "openmsx.hh" namespace openmsx { namespace SCSI { // Group 0: 6bytes cdb static const byte OP_TEST_UNIT_READY = 0x00; static const byte OP_REZERO_UNIT = 0x01; static const byte OP_REQUEST_SENSE = 0x03; static const byte OP_FORMAT_UNIT = 0x04; static const byte OP_REASSIGN_BLOCKS = 0x07; static const byte OP_READ6 = 0x08; static const byte OP_WRITE6 = 0x0A; static const byte OP_SEEK6 = 0x0B; static const byte OP_INQUIRY = 0x12; static const byte OP_RESERVE_UNIT = 0x16; static const byte OP_RELEASE_UNIT = 0x17; static const byte OP_MODE_SENSE = 0x1A; static const byte OP_START_STOP_UNIT = 0x1B; static const byte OP_SEND_DIAGNOSTIC = 0x1D; // Group 1: 10bytes cdb static const byte OP_GROUP1 = 0x20; static const byte OP_READ_CAPACITY = 0x25; static const byte OP_READ10 = 0x28; static const byte OP_WRITE10 = 0x2A; static const byte OP_SEEK10 = 0x2B; static const byte OP_GROUP2 = 0x40; static const byte OP_CHANGE_DEFINITION = 0x40; static const byte OP_READ_SUB_CHANNEL = 0x42; static const byte OP_READ_TOC = 0x43; static const byte OP_READ_HEADER = 0x44; static const byte OP_PLAY_AUDIO = 0x45; static const byte OP_PLAY_AUDIO_MSF = 0x47; static const byte OP_PLAY_TRACK_INDEX = 0x48; static const byte OP_PLAY_TRACK_RELATIVE = 0x49; static const byte OP_PAUSE_RESUME = 0x4B; static const byte OP_PLAY_AUDIO12 = 0xA5; static const byte OP_READ12 = 0xA8; static const byte OP_PLAY_TRACK_RELATIVE12 = 0xA9; static const byte OP_READ_CD_MSF = 0xB9; static const byte OP_READ_CD = 0xBE; // Sense data KEY | ASC | ASCQ static const unsigned SENSE_NO_SENSE = 0x000000; static const unsigned SENSE_NOT_READY = 0x020400; static const unsigned SENSE_MEDIUM_NOT_PRESENT = 0x023a00; static const unsigned SENSE_UNRECOVERED_READ_ERROR = 0x031100; static const unsigned SENSE_WRITE_FAULT = 0x040300; static const unsigned SENSE_INVALID_COMMAND_CODE = 0x052000; static const unsigned SENSE_ILLEGAL_BLOCK_ADDRESS = 0x052100; static const unsigned SENSE_INVALID_LUN = 0x052500; static const unsigned SENSE_POWER_ON = 0x062900; static const unsigned SENSE_WRITE_PROTECT = 0x072700; static const unsigned SENSE_MESSAGE_REJECT_ERROR = 0x0b4300; static const unsigned SENSE_INITIATOR_DETECTED_ERR = 0x0b4800; static const unsigned SENSE_ILLEGAL_MESSAGE = 0x0b4900; // Message static const byte MSG_COMMAND_COMPLETE = 0x00; static const byte MSG_INITIATOR_DETECT_ERROR = 0x05; static const byte MSG_ABORT = 0x06; static const byte MSG_REJECT = 0x07; static const byte MSG_NO_OPERATION = 0x08; static const byte MSG_PARITY_ERROR = 0x09; static const byte MSG_BUS_DEVICE_RESET = 0x0c; // Status static const byte ST_GOOD = 0; static const byte ST_CHECK_CONDITION = 2; static const byte ST_BUSY = 8; // Device type static const byte DT_DirectAccess = 0x00; static const byte DT_SequencialAccess = 0x01; static const byte DT_Printer = 0x02; static const byte DT_Processor = 0x03; static const byte DT_WriteOnce = 0x04; static const byte DT_CDROM = 0x05; static const byte DT_Scanner = 0x06; static const byte DT_OpticalMemory = 0x07; static const byte DT_MediaChanger = 0x08; static const byte DT_Communications = 0x09; static const byte DT_Undefined = 0x1f; enum Phase { UNDEFINED, // used in MB89532 BUS_FREE, ARBITRATION, SELECTION, RESELECTION, COMMAND, EXECUTE, DATA_IN, DATA_OUT, STATUS, MSG_OUT, MSG_IN, }; } // namespace SCSI } // namespace openmsx #endif openmsx-0.10.0/src/ide/IDEHD.hh0000644000175000017500000000156312262345041016537 0ustar manuelmanuel00000000000000#ifndef IDEHD_HH #define IDEHD_HH #include "HD.hh" #include "AbstractIDEDevice.hh" #include "serialize_meta.hh" #include "noncopyable.hh" namespace openmsx { class DeviceConfig; class DiskManipulator; class IDEHD : public HD, public AbstractIDEDevice, private noncopyable { public: explicit IDEHD(const DeviceConfig& config); virtual ~IDEHD(); template void serialize(Archive& ar, unsigned version); private: // AbstractIDEDevice: virtual bool isPacketDevice(); virtual const std::string& getDeviceName(); virtual void fillIdentifyBlock (AlignedBuffer& buffer); virtual unsigned readBlockStart(AlignedBuffer& buffer, unsigned count); virtual void writeBlockComplete(AlignedBuffer& buffer, unsigned count); virtual void executeCommand(byte cmd); DiskManipulator& diskManipulator; unsigned transferSectorNumber; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/SCSIDevice.hh0000644000175000017500000000254012262345041017577 0ustar manuelmanuel00000000000000#ifndef SCSIDEVICE_HH #define SCSIDEVICE_HH #include "SCSI.hh" namespace openmsx { class SCSIDevice { public: static const unsigned BIT_SCSI2 = 0x0001; static const unsigned BIT_SCSI2_ONLY = 0x0002; static const unsigned BIT_SCSI3 = 0x0004; static const unsigned MODE_SCSI1 = 0x0000; static const unsigned MODE_SCSI2 = 0x0003; static const unsigned MODE_SCSI3 = 0x0005; static const unsigned MODE_UNITATTENTION = 0x0008; // report unit attention static const unsigned MODE_MEGASCSI = 0x0010; // report disk change when call of // 'test unit ready' static const unsigned MODE_NOVAXIS = 0x0100; static const unsigned BUFFER_SIZE = 0x10000; // 64KB virtual ~SCSIDevice() {}; virtual void reset() = 0; virtual bool isSelected() = 0; virtual unsigned executeCmd(const byte* cdb, SCSI::Phase& phase, unsigned& blocks) = 0; virtual unsigned executingCmd(SCSI::Phase& phase, unsigned& blocks) = 0; virtual byte getStatusCode() = 0; virtual int msgOut(byte value) = 0; virtual byte msgIn() = 0; virtual void disconnect() = 0; virtual void busReset() = 0; // only used in MB89352 controller virtual unsigned dataIn(unsigned& blocks) = 0; virtual unsigned dataOut(unsigned& blocks) = 0; }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/GoudaSCSI.cc0000644000175000017500000000336012262345041017426 0ustar manuelmanuel00000000000000#include "GoudaSCSI.hh" #include "WD33C93.hh" #include "Rom.hh" #include "serialize.hh" #include "unreachable.hh" #include "memory.hh" namespace openmsx { GoudaSCSI::GoudaSCSI(const DeviceConfig& config) : MSXDevice(config) , rom(make_unique(getName() + " ROM", "rom", config)) , wd33c93(make_unique(config)) { reset(EmuTime::dummy()); } GoudaSCSI::~GoudaSCSI() { } void GoudaSCSI::reset(EmuTime::param /*time*/) { wd33c93->reset(true); } byte GoudaSCSI::readIO(word port, EmuTime::param /*time*/) { switch (port & 0x03) { case 0: return wd33c93->readAuxStatus(); case 1: return wd33c93->readCtrl(); case 2: return 0xb0; // bit 4: 1 = Halt on SCSI parity error default: UNREACHABLE; return 0; } } byte GoudaSCSI::peekIO(word port, EmuTime::param /*time*/) const { switch (port & 0x03) { case 0: return wd33c93->peekAuxStatus(); case 1: return wd33c93->peekCtrl(); case 2: return 0xb0; // bit 4: 1 = Halt on SCSI parity error default: UNREACHABLE; return 0; } } void GoudaSCSI::writeIO(word port, byte value, EmuTime::param time) { switch (port & 0x03) { case 0: wd33c93->writeAdr(value); break; case 1: wd33c93->writeCtrl(value); break; case 2: reset(time); break; default: UNREACHABLE; } } byte GoudaSCSI::readMem(word address, EmuTime::param /*time*/) { return *GoudaSCSI::getReadCacheLine(address); } const byte* GoudaSCSI::getReadCacheLine(word start) const { return &(*rom)[start & (rom->getSize() - 1)]; } template void GoudaSCSI::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("WD33C93", *wd33c93); } INSTANTIATE_SERIALIZE_METHODS(GoudaSCSI); REGISTER_MSXDEVICE(GoudaSCSI, "GoudaSCSI"); } // namespace openmsx openmsx-0.10.0/src/ide/HDImageCLI.cc0000644000175000017500000000177512262345041017503 0ustar manuelmanuel00000000000000#include "HDImageCLI.hh" #include "CommandLineParser.hh" #include "GlobalCommandController.hh" #include "TclObject.hh" #include "MSXException.hh" using std::deque; using std::string; namespace openmsx { HDImageCLI::HDImageCLI(CommandLineParser& parser_) : parser(parser_) { parser.registerOption("-hda", *this); // TODO: offer more options in case you want to specify 2 hard disk images? } void HDImageCLI::parseOption(const string& option, deque& cmdLine) { string_ref hd = string_ref(option).substr(1); // hda string filename = getArgument(option, cmdLine); if (!parser.getGlobalCommandController().hasCommand(hd)) { // TODO WIP throw MSXException("No hard disk named '" + hd + "'."); } TclObject command(parser.getGlobalCommandController().getInterpreter()); command.addListElement(hd); command.addListElement(filename); command.executeCommand(); } string_ref HDImageCLI::optionHelp() const { return "Use hard disk image in argument for the IDE or SCSI extensions"; } } // namespace openmsx openmsx-0.10.0/src/ide/MB89352.hh0000644000175000017500000000423212262345041016627 0ustar manuelmanuel00000000000000/* Ported from: ** Source: /cvsroot/bluemsx/blueMSX/Src/IoDevice/MB89352.h,v ** Revision: 1.4 ** Date: 2007/03/28 17:35:35 ** ** More info: http://www.bluemsx.com ** ** Copyright (C) 2003-2007 Daniel Vik, white cat */ #ifndef MB89352_HH #define MB89352_HH #include "SCSI.hh" #include "SCSIDevice.hh" #include "AlignedBuffer.hh" #include namespace openmsx { class DeviceConfig; class MSXMotherBoard; class MB89352 { public: explicit MB89352(const DeviceConfig& config); ~MB89352(); void reset(bool scsireset); byte readRegister(byte reg); byte peekRegister(byte reg) const; byte readDREG(); byte peekDREG() const; void writeRegister(byte reg, byte value); void writeDREG(byte value); template void serialize(Archive& ar, unsigned version); private: void disconnect(); void softReset(); void setACKREQ(byte& value); void resetACKREQ(); byte getSSTS() const; std::unique_ptr dev[8]; AlignedByteArray buffer; // buffer for transfer unsigned cdbIdx; // cdb index unsigned bufIdx; // buffer index int msgin; // Message In flag int counter; // read and written number of bytes // within the range in the buffer unsigned blockCounter; // Number of blocks outside buffer // (512bytes / block) int tc; // counter for hardware transfer SCSI::Phase phase; // SCSI::Phase nextPhase; // for message system byte myId; // SPC SCSI ID 0..7 byte targetId; // SCSI Device target ID 0..7 byte regs[16]; // SPC register bool rst; // SCSI bus reset signal byte atn; // SCSI bus attention signal bool isEnabled; // spc enable flag bool isBusy; // spc now working bool isTransfer; // hardware transfer mode //TODO: bool devBusy; // CDROM busy (buffer conflict prevention) byte cdb[12]; // Command Descripter Block }; } // namespace openmsx #endif openmsx-0.10.0/src/ide/IDEHD.cc0000644000175000017500000000655612262345041016534 0ustar manuelmanuel00000000000000#include "IDEHD.hh" #include "MSXException.hh" #include "DeviceConfig.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" #include "DiskManipulator.hh" #include "endian.hh" #include "serialize.hh" #include namespace openmsx { IDEHD::IDEHD(const DeviceConfig& config) : HD(config) , AbstractIDEDevice(config.getMotherBoard()) , diskManipulator(config.getReactor().getDiskManipulator()) { diskManipulator.registerDrive(*this, config.getMotherBoard().getMachineID() + "::"); } IDEHD::~IDEHD() { diskManipulator.unregisterDrive(*this); } bool IDEHD::isPacketDevice() { return false; } const std::string& IDEHD::getDeviceName() { static const std::string NAME = "OPENMSX HARD DISK"; return NAME; } void IDEHD::fillIdentifyBlock(AlignedBuffer& buffer) { auto totalSectors = getNbSectors(); uint16_t heads = 16; uint16_t sectors = 32; uint16_t cylinders = uint16_t(totalSectors / (heads * sectors)); // TODO overflow? Endian::writeL16(&buffer[1 * 2], cylinders); Endian::writeL16(&buffer[3 * 2], heads); Endian::writeL16(&buffer[6 * 2], sectors); buffer[47 * 2 + 0] = 16; // max sector transfer per interrupt buffer[47 * 2 + 1] = 0x80; // specced value // .... 1...: IORDY supported (hardware signal used by PIO modes >3) // .... ..1.: LBA supported buffer[49 * 2 + 1] = 0x0A; // TODO check for overflow Endian::writeL32(&buffer[60 * 2], unsigned(totalSectors)); } unsigned IDEHD::readBlockStart(AlignedBuffer& buffer, unsigned count) { try { assert(count >= 512); (void)count; // avoid warning readSector(transferSectorNumber, *aligned_cast(buffer)); ++transferSectorNumber; return 512; } catch (MSXException&) { abortReadTransfer(UNC); return 0; } } void IDEHD::writeBlockComplete(AlignedBuffer& buffer, unsigned count) { try { assert((count % 512) == 0); unsigned num = count / 512; for (unsigned i = 0; i < num; ++i) { writeSector(transferSectorNumber++, *aligned_cast(buffer + 512 * i)); } } catch (MSXException&) { abortWriteTransfer(UNC); } } void IDEHD::executeCommand(byte cmd) { switch (cmd) { case 0x20: // Read Sector case 0x30: { // Write Sector unsigned sectorNumber = getSectorNumber(); unsigned numSectors = getNumSectors(); if ((sectorNumber + numSectors) > getNbSectors()) { // Note: The original code set ABORT as well, but that is not // allowed according to the spec. setError(IDNF); break; } transferSectorNumber = sectorNumber; if (cmd == 0x20) { startLongReadTransfer(numSectors * 512); } else { startWriteTransfer(numSectors * 512); } break; } case 0xF8: // Read Native Max Address // We don't support the Host Protected Area feature set, but SymbOS // uses only this particular command, so we support just this one. // TODO this only supports 28-bit sector numbers setSectorNumber(unsigned(getNbSectors())); break; default: // all others AbstractIDEDevice::executeCommand(cmd); } } template void IDEHD::serialize(Archive& ar, unsigned /*version*/) { // don't serialize SectorAccessibleDisk, DiskContainer base classes ar.template serializeBase(*this); ar.template serializeBase(*this); ar.serialize("transferSectorNumber", transferSectorNumber); } INSTANTIATE_SERIALIZE_METHODS(IDEHD); REGISTER_POLYMORPHIC_INITIALIZER(IDEDevice, IDEHD, "IDEHD"); } // namespace openmsx openmsx-0.10.0/src/ide/DummySCSIDevice.cc0000644000175000017500000000237712262345041020611 0ustar manuelmanuel00000000000000#include "DummySCSIDevice.hh" #include "serialize.hh" namespace openmsx { void DummySCSIDevice::reset() { // do nothing } bool DummySCSIDevice::isSelected() { return false; } unsigned DummySCSIDevice::executeCmd( const byte* /*cdb*/, SCSI::Phase& /*phase*/, unsigned& /*blocks*/) { // do nothing return 0; } unsigned DummySCSIDevice::executingCmd(SCSI::Phase& /*phase*/, unsigned& /*blocks*/) { return 0; } byte DummySCSIDevice::getStatusCode() { return SCSI::ST_CHECK_CONDITION; } int DummySCSIDevice::msgOut(byte /*value*/) { return 0; // TODO: check if this is sane, but it doesn't seem to be used anyway } byte DummySCSIDevice::msgIn() { return 0; // TODO: check if this is sane, but it doesn't seem to be used anyway } void DummySCSIDevice::disconnect() { // do nothing } void DummySCSIDevice::busReset() { // do nothing } unsigned DummySCSIDevice::dataIn(unsigned& blocks) { blocks = 0; return 0; } unsigned DummySCSIDevice::dataOut(unsigned& blocks) { blocks = 0; return 0; } template void DummySCSIDevice::serialize(Archive& /*ar*/, unsigned /*version*/) { // nothing } INSTANTIATE_SERIALIZE_METHODS(DummySCSIDevice); REGISTER_POLYMORPHIC_INITIALIZER(SCSIDevice, DummySCSIDevice, "DummySCSIDevice"); } // namespace openmsx openmsx-0.10.0/src/ide/SunriseIDE.hh0000644000175000017500000000250112262345041017665 0ustar manuelmanuel00000000000000#ifndef SUNRISEIDE_HH #define SUNRISEIDE_HH #include "MSXDevice.hh" #include namespace openmsx { class IDEDevice; class Rom; class RomBlockDebuggable; class SunriseIDE : public MSXDevice { public: explicit SunriseIDE(const DeviceConfig& config); virtual ~SunriseIDE(); virtual void powerUp(EmuTime::param time); virtual void reset(EmuTime::param time); virtual byte readMem(word address, EmuTime::param time); virtual void writeMem(word address, byte value, EmuTime::param time); virtual const byte* getReadCacheLine(word start) const; template void serialize(Archive& ar, unsigned version); private: void writeControl(byte value); byte readDataLow(EmuTime::param time); byte readDataHigh(EmuTime::param time); word readData(EmuTime::param time); byte readReg(nibble reg, EmuTime::param time); void writeDataLow(byte value); void writeDataHigh(byte value, EmuTime::param time); void writeData(word value, EmuTime::param time); void writeReg(nibble reg, byte value, EmuTime::param time); const std::unique_ptr rom; const std::unique_ptr romBlockDebug; std::unique_ptr device[2]; const byte* internalBank; byte readLatch; byte writeLatch; byte selectedDevice; byte control; bool ideRegsEnabled; bool softReset; }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/0000755000175000017500000000000012262345041016446 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/cassette/CassettePlayerCLI.cc0000644000175000017500000000234412262345041022240 0ustar manuelmanuel00000000000000#include "CassettePlayerCLI.hh" #include "CommandLineParser.hh" #include "GlobalCommandController.hh" #include "MSXException.hh" #include "TclObject.hh" using std::deque; using std::string; namespace openmsx { CassettePlayerCLI::CassettePlayerCLI(CommandLineParser& parser_) : parser(parser_) { parser.registerOption("-cassetteplayer", *this); parser.registerFileType("cas,wav", *this); } void CassettePlayerCLI::parseOption(const string& option, deque& cmdLine) { parseFileType(getArgument(option, cmdLine), cmdLine); } string_ref CassettePlayerCLI::optionHelp() const { return "Put cassette image specified in argument in " "virtual cassetteplayer"; } void CassettePlayerCLI::parseFileType(const string& filename, deque& /*cmdLine*/) { if (!parser.getGlobalCommandController().hasCommand("cassetteplayer")) { throw MSXException("No cassetteplayer."); } TclObject command(parser.getGlobalCommandController().getInterpreter()); command.addListElement("cassetteplayer"); command.addListElement(filename); command.executeCommand(); } string_ref CassettePlayerCLI::fileTypeHelp() const { return "Cassette image, raw recording or fMSX CAS image"; } } // namespace openmsx openmsx-0.10.0/src/cassette/CassettePort.hh0000644000175000017500000000521112262345041021406 0ustar manuelmanuel00000000000000#ifndef CASSETTEPORT_HH #define CASSETTEPORT_HH #include "Connector.hh" #include "components.hh" #include namespace openmsx { class HardwareConfig; class MSXMotherBoard; class CassetteDevice; #if COMPONENT_LASERDISC class LaserdiscPlayer; #endif class CassettePortInterface { public: virtual ~CassettePortInterface(); /** * Sets the cassette motor relay * false = off true = on */ virtual void setMotor(bool status, EmuTime::param time) = 0; /** * Writes one bit to the cassette port. * From the RedBook: * The CasOut bit is filtered and attenuated before being * taken to the cassette DIN socket as the MIC signal. All * cassette tone generation is performed in software. */ virtual void cassetteOut(bool output, EmuTime::param time) = 0; /** * last bit written to CasOut. * for use in Pluggable::plugHelper() */ virtual bool lastOut() const = 0; /** * Reads one bit from the cassette port. * From the RedBook: * The cassette input is used to read the signal from the * cassette EAR output. This is passed through a comparator * to clean the edges and to convert to digital levels, * but is otherwise unprocessed. */ virtual bool cassetteIn(EmuTime::param time) = 0; #if COMPONENT_LASERDISC /** * Set the Laserdisc Player; when the motor control is off, sound * is read from the laserdisc. */ virtual void setLaserdiscPlayer(LaserdiscPlayer *laserdisc) = 0; #endif }; class CassettePort : public CassettePortInterface, public Connector { public: explicit CassettePort(const HardwareConfig& hwConf); virtual ~CassettePort(); virtual void setMotor(bool status, EmuTime::param time); virtual void cassetteOut(bool output, EmuTime::param time); virtual bool cassetteIn(EmuTime::param time); #if COMPONENT_LASERDISC virtual void setLaserdiscPlayer(LaserdiscPlayer *laserdisc); #endif virtual bool lastOut() const; // Connector virtual const std::string getDescription() const; virtual string_ref getClass() const; virtual void unplug(EmuTime::param time); template void serialize(Archive& ar, unsigned version); private: CassetteDevice& getPluggedCasDev() const; MSXMotherBoard& motherBoard; #if COMPONENT_LASERDISC LaserdiscPlayer* laserdiscPlayer; #endif bool lastOutput; bool motorControl; }; class DummyCassettePort : public CassettePortInterface { public: virtual void setMotor(bool status, EmuTime::param time); virtual void cassetteOut(bool output, EmuTime::param time); virtual bool cassetteIn(EmuTime::param time); #if COMPONENT_LASERDISC virtual void setLaserdiscPlayer(LaserdiscPlayer *laserdisc); #endif virtual bool lastOut() const; }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/node.mk0000644000175000017500000000031312262345041017721 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR:= \ CassetteDevice \ CassettePlayer \ CassettePlayerCLI \ CassettePort \ DummyCassetteDevice \ CassetteImage \ CasImage \ WavImage include build/node-end.mk openmsx-0.10.0/src/cassette/DummyCassetteDevice.hh0000644000175000017500000000103112262345041022671 0ustar manuelmanuel00000000000000#ifndef DUMMYCASSETTEDEVICE_HH #define DUMMYCASSETTEDEVICE_HH #include "CassetteDevice.hh" namespace openmsx { class DummyCassetteDevice : public CassetteDevice { public: virtual void setMotor(bool status, EmuTime::param time); virtual void setSignal(bool output, EmuTime::param time); virtual short readSample(EmuTime::param time); virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/CassettePlayer.hh0000644000175000017500000001052712262345041021724 0ustar manuelmanuel00000000000000#ifndef CASSETTEPLAYER_HH #define CASSETTEPLAYER_HH #include "EventListener.hh" #include "CassetteDevice.hh" #include "ResampledSoundDevice.hh" #include "Schedulable.hh" #include "Filename.hh" #include "EmuTime.hh" #include "serialize_meta.hh" #include #include namespace openmsx { class CassetteImage; class HardwareConfig; class MSXMotherBoard; class Wav8Writer; class LoadingIndicator; class BooleanSetting; class TapeCommand; class CassettePlayer : public CassetteDevice, public ResampledSoundDevice , private EventListener, private Schedulable { public: explicit CassettePlayer(const HardwareConfig& hwConf); virtual ~CassettePlayer(); // CassetteDevice virtual void setMotor(bool status, EmuTime::param time); virtual short readSample(EmuTime::param time); virtual void setSignal(bool output, EmuTime::param time); // Pluggable virtual const std::string& getName() const; virtual string_ref getDescription() const; virtual void plugHelper(Connector& connector, EmuTime::param time); virtual void unplugHelper(EmuTime::param time); // SoundDevice virtual void generateChannels(int** bufs, unsigned num); template void serialize(Archive& ar, unsigned version); enum State { PLAY, RECORD, STOP }; // public for serialization private: State getState() const; std::string getStateString() const; void setState(State newState, const Filename& newImage, EmuTime::param time); void setImageName(const Filename& newImage); const Filename& getImageName() const; void checkInvariants() const; /** Insert a tape for use in PLAY mode. */ void playTape(const Filename& filename, EmuTime::param time); void insertTape(const Filename& filename); /** Removes tape (possibly stops recording). And go to STOP mode. */ void removeTape(EmuTime::param time); /** Goes to RECORD mode using the given filename as a new tape * image. Finishes any old recording session. */ void recordTape(const Filename& filename, EmuTime::param time); /** Rewinds the tape. Also sets PLAY mode, because you can't record * over an existing tape. (And it won't be useful to implement that * anyway.) */ void rewind(EmuTime::param time); /** Enable or disable motor control. */ void setMotorControl(bool status, EmuTime::param time); /** True when the tape is rolling: not in STOP mode, AND [ motorcontrol * is disabled OR motor is on ]. */ bool isRolling() const; /** If motor, motorControl or state is changed, this method should * be called to update the end-of-tape syncpoint and the loading * indicator. */ void updateLoadingState(EmuTime::param time); /** Returns the position of the tape, in seconds from the * beginning of the tape. */ double getTapePos(EmuTime::param time); /** Returns the length of the tape in seconds. * When no tape is inserted, this returns 0. While recording this * returns the current position (so while recording, tape length grows * continuously). */ double getTapeLength(EmuTime::param time); void sync(EmuTime::param time); void updateTapePosition(EmuDuration::param duration, EmuTime::param time); void generateRecordOutput(EmuDuration::param duration); void fillBuf(size_t length, double x); void flushOutput(); void autoRun(); // EventListener virtual int signalEvent(const std::shared_ptr& event); // Schedulable virtual void executeUntil(EmuTime::param time, int userData); static const size_t BUF_SIZE = 1024; unsigned char buf[BUF_SIZE]; double lastX; // last unfiltered output double lastY; // last filtered output double partialOut; double partialInterval; /** The time in the world of the tape. Zero at the start of the tape. */ EmuTime tapePos; /** Last time the sync() method was called. * Used to calculate EmuDuration since last sync. */ EmuTime prevSyncTime; // SoundDevice unsigned audioPos; Filename casImage; MSXMotherBoard& motherBoard; const std::unique_ptr tapeCommand; const std::unique_ptr loadingIndicator; const std::unique_ptr autoRunSetting; std::unique_ptr recordImage; std::unique_ptr playImage; size_t sampcnt; State state; bool lastOutput; bool motor, motorControl; bool syncScheduled; friend class TapeCommand; }; SERIALIZE_CLASS_VERSION(CassettePlayer, 2); } // namespace openmsx #endif openmsx-0.10.0/src/cassette/CasImage.cc0000644000175000017500000001463112262345041020433 0ustar manuelmanuel00000000000000#include "CasImage.hh" #include "File.hh" #include "Filename.hh" #include "CliComm.hh" #include "Clock.hh" #include "MSXException.hh" #include "xrange.hh" #include // for memcmp namespace openmsx { // output settings // a higher baudrate doesn't work anymore, but it is unclear why, because 4600 // baud should work (known from Speedsave 4000 and Turbo 5000 programs). // 3765 still works on a Toshiba HX-10 and Philips NMS 8250, but not on a // Panasonic FS-A1WSX, on which 3763 is the max. National CF-2000 has 3762 as // the max. Let's take 3760 then as a safe value. // UPDATE: that seems to break RUN"CAS:" type of programs. 3744 seems to work // for those as well (we don't understand why yet) static const unsigned BAUDRATE = 3744; static const unsigned OUTPUT_FREQUENCY = 4 * BAUDRATE; // 4 samples per bit // We oversample the audio signal for better sound quality (especially in // combination with the hq resampler). Without oversampling the audio output // could contain portions like this: // -1, +1, -1, +1, -1, +1, ... // So it contains a signal at the Nyquist frequency. The hq resampler contains // a low-pass filter, and (all practically implementable) filters cut off a // portion of the spectrum near the Nyquist freq. So this high freq signal was // lost after the hq resampler. After oversampling, the signal looks like this: // -1, -1, -1, -1, +1, +1, +1, +1, -1, -1, -1, -1, ... // So every sample repeated 4 times. static const unsigned AUDIO_OVERSAMPLE = 4; // number of output bytes for silent parts static const unsigned SHORT_SILENCE = OUTPUT_FREQUENCY * 1; // 1 second static const unsigned LONG_SILENCE = OUTPUT_FREQUENCY * 2; // 2 seconds // number of 1-bits for headers static const unsigned LONG_HEADER = 16000 / 2; static const unsigned SHORT_HEADER = 4000 / 2; // headers definitions static const byte CAS_HEADER [ 8] = { 0x1F,0xA6,0xDE,0xBA,0xCC,0x13,0x7D,0x74 }; static const byte ASCII_HEADER [10] = { 0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA,0xEA }; static const byte BINARY_HEADER[10] = { 0xD0,0xD0,0xD0,0xD0,0xD0,0xD0,0xD0,0xD0,0xD0,0xD0 }; static const byte BASIC_HEADER [10] = { 0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3,0xD3 }; CasImage::CasImage(const Filename& filename, FilePool& filePool, CliComm& cliComm) { setFirstFileType(CassetteImage::UNKNOWN); convert(filename, filePool, cliComm); } short CasImage::getSampleAt(EmuTime::param time) { static const Clock zero(EmuTime::zero); unsigned pos = zero.getTicksTill(time); return pos < output.size() ? output[pos] * 256 : 0; } EmuTime CasImage::getEndTime() const { Clock clk(EmuTime::zero); clk += unsigned(output.size()); return clk.getTime(); } unsigned CasImage::getFrequency() const { return OUTPUT_FREQUENCY * AUDIO_OVERSAMPLE; } void CasImage::fillBuffer(unsigned pos, int** bufs, unsigned num) const { size_t nbSamples = output.size(); if ((pos / AUDIO_OVERSAMPLE) < nbSamples) { for (auto i : xrange(num)) { bufs[0][i] = ((pos / AUDIO_OVERSAMPLE) < nbSamples) ? output[pos / AUDIO_OVERSAMPLE] * 256 : 0; ++pos; } } else { bufs[0] = nullptr; } } void CasImage::write0() { output.push_back( 127); output.push_back( 127); output.push_back(-127); output.push_back(-127); } void CasImage::write1() { output.push_back( 127); output.push_back(-127); output.push_back( 127); output.push_back(-127); } // write a header signal void CasImage::writeHeader(int s) { for (int i = 0; i < s; ++i) { write1(); } } // write silence void CasImage::writeSilence(int s) { output.insert(output.end(), s, 0); } // write a byte void CasImage::writeByte(byte b) { // one start bit write0(); // eight data bits for (auto i : xrange(8)) { if (b & (1 << i)) { write1(); } else { write0(); } } // two stop bits write1(); write1(); } // write data until a header is detected bool CasImage::writeData(const byte* buf, size_t size, size_t& pos) { bool eof = false; while ((pos + 8) <= size) { if (!memcmp(&buf[pos], CAS_HEADER, 8)) { return eof; } writeByte(buf[pos]); if (buf[pos] == 0x1A) { eof = true; } pos++; } while (pos < size) { writeByte(buf[pos++]); } return false; } void CasImage::convert(const Filename& filename, FilePool& filePool, CliComm& cliComm) { File file(filename); size_t size; const byte* buf = file.mmap(size); // search for a header in the .cas file bool issueWarning = false; bool headerFound = false; bool firstFile = true; size_t pos = 0; while ((pos + 8) <= size) { if (!memcmp(&buf[pos], CAS_HEADER, 8)) { // it probably works fine if a long header is used for every // header but since the msx bios makes a distinction between // them, we do also (hence a lot of code). headerFound = true; pos += 8; writeSilence(LONG_SILENCE); writeHeader(LONG_HEADER); if ((pos + 10) <= size) { // determine file type FileType type = CassetteImage::UNKNOWN; if (!memcmp(&buf[pos], ASCII_HEADER, 10)) { type = CassetteImage::ASCII; } else if (!memcmp(&buf[pos], BINARY_HEADER, 10)) { type = CassetteImage::BINARY; } else if (!memcmp(&buf[pos], BASIC_HEADER, 10)) { type = CassetteImage::BASIC; } if (firstFile) setFirstFileType(type); switch (type) { case CassetteImage::ASCII: writeData(buf, size, pos); bool eof; do { pos += 8; writeSilence(SHORT_SILENCE); writeHeader(SHORT_HEADER); eof = writeData(buf, size, pos); } while (!eof && ((pos + 8) <= size)); break; case CassetteImage::BINARY: case CassetteImage::BASIC: writeData(buf, size, pos); writeSilence(SHORT_SILENCE); writeHeader(SHORT_HEADER); pos += 8; writeData(buf, size, pos); break; default: // unknown file type: using long header writeData(buf, size, pos); break; } } else { // unknown file type: using long header writeData(buf, size, pos); } firstFile = false; } else { // should not occur PRT_DEBUG("CAS2WAV: skipping unhandled data"); pos++; issueWarning = true; } } if (!headerFound) { throw MSXException(filename.getOriginal() + ": not a valid CAS image"); } if (issueWarning) { cliComm.printWarning("Skipped unhandled data in " + filename.getOriginal()); } // conversion successful, now calc sha1sum file.setFilePool(filePool); setSha1Sum(file.getSha1Sum()); } } // namespace openmsx openmsx-0.10.0/src/cassette/CassetteDevice.cc0000644000175000017500000000022412262345041021646 0ustar manuelmanuel00000000000000#include "CassetteDevice.hh" namespace openmsx { string_ref CassetteDevice::getClass() const { return "Cassette Port"; } } // namespace openmsx openmsx-0.10.0/src/cassette/WavImage.cc0000644000175000017500000000335112262345041020457 0ustar manuelmanuel00000000000000#include "WavImage.hh" #include "LocalFileReference.hh" #include "File.hh" #include "Math.hh" #include "memory.hh" #include "xrange.hh" namespace openmsx { // Note: type detection not implemented yet for WAV images WavImage::WavImage(const Filename& filename, FilePool& filePool) : clock(EmuTime::zero) { std::unique_ptr localFile; { // File object must be destroyed before localFile is actually // used by an external API (see comments in LocalFileReference // for details). File file(filename); file.setFilePool(filePool); setSha1Sum(file.getSha1Sum()); localFile = make_unique(file); } wav = WavData(localFile->getFilename(), 16, 0); clock.setFreq(wav.getFreq()); // calculate the average to subtract it later (simple DC filter) auto nbSamples = wav.getSize(); if (nbSamples > 0) { int64_t total = 0; for (auto i : xrange(nbSamples)) { total += getSample(i); } average = short(total / nbSamples); } else { average = 0; } } WavImage::~WavImage() { } int WavImage::getSample(unsigned pos) const { if (pos < wav.getSize()) { auto buf = static_cast(wav.getData()); return buf[pos]; } return 0; } short WavImage::getSampleAt(EmuTime::param time) { unsigned pos = clock.getTicksTill(time); return Math::clipIntToShort(getSample(pos) - average); } EmuTime WavImage::getEndTime() const { DynamicClock clk(clock); clk += wav.getSize(); return clk.getTime(); } unsigned WavImage::getFrequency() const { return clock.getFreq(); } void WavImage::fillBuffer(unsigned pos, int** bufs, unsigned num) const { if (pos < wav.getSize()) { for (auto i : xrange(num)) { bufs[0][i] = getSample(pos + i); } } else { bufs[0] = nullptr; } } } // namespace openmsx openmsx-0.10.0/src/cassette/CassetteImage.hh0000644000175000017500000000213712262345041021510 0ustar manuelmanuel00000000000000#ifndef CASSETTEIMAGE_HH #define CASSETTEIMAGE_HH #include "EmuTime.hh" #include "sha1.hh" #include namespace openmsx { class CassetteImage { public: enum FileType { ASCII, BINARY, BASIC, UNKNOWN }; virtual ~CassetteImage(); virtual short getSampleAt(EmuTime::param time) = 0; virtual EmuTime getEndTime() const = 0; virtual unsigned getFrequency() const = 0; virtual void fillBuffer(unsigned pos, int** bufs, unsigned num) const = 0; FileType getFirstFileType() const; std::string getFirstFileTypeAsString() const; /** Get sha1sum for this image. * This is based on the content of the file, not the logical meaning of * the file. IOW: it's possible for different files (with different * sha1sum) to represent the same logical cassette data (e.g. wav with * different bits per sample). This method will give a different * sha1sum to such files. */ const Sha1Sum& getSha1Sum() const; protected: CassetteImage(); void setFirstFileType(FileType type); void setSha1Sum(const Sha1Sum& sha1sum); private: FileType firstFileType; Sha1Sum sha1sum; }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/CassettePort.cc0000644000175000017500000000607712262345041021407 0ustar manuelmanuel00000000000000#include "CassettePort.hh" #include "CassetteDevice.hh" #include "CassettePlayer.hh" #include "components.hh" #if COMPONENT_LASERDISC #include "LaserdiscPlayer.hh" #endif #include "DummyCassetteDevice.hh" #include "HardwareConfig.hh" #include "MSXMotherBoard.hh" #include "PluggingController.hh" #include "checked_cast.hh" #include "serialize.hh" #include "memory.hh" using std::unique_ptr; using std::string; namespace openmsx { // CassettePortInterface CassettePortInterface::~CassettePortInterface() { } // DummyCassettePort void DummyCassettePort::setMotor(bool /*status*/, EmuTime::param /*time*/) { // do nothing } void DummyCassettePort::cassetteOut(bool /*output*/, EmuTime::param /*time*/) { // do nothing } bool DummyCassettePort::cassetteIn(EmuTime::param /*time*/) { return false; } bool DummyCassettePort::lastOut() const { return false; // not relevant } #if COMPONENT_LASERDISC void DummyCassettePort::setLaserdiscPlayer(LaserdiscPlayer* /* laserdisc */) { // do nothing } #endif // CassettePort CassettePort::CassettePort(const HardwareConfig& hwConf) : Connector(hwConf.getMotherBoard().getPluggingController(), "cassetteport", make_unique()) , motherBoard(hwConf.getMotherBoard()) #if COMPONENT_LASERDISC , laserdiscPlayer(nullptr) #endif , lastOutput(false) , motorControl(false) { getPluggingController().registerPluggable( make_unique(hwConf)); } CassettePort::~CassettePort() { unplug(motherBoard.getCurrentTime()); } void CassettePort::setMotor(bool status, EmuTime::param time) { // TODO make 'click' sound motorControl = status; getPluggedCasDev().setMotor(status, time); } void CassettePort::cassetteOut(bool output, EmuTime::param time) { lastOutput = output; // leave everything to the pluggable getPluggedCasDev().setSignal(output, time); } bool CassettePort::lastOut() const { return lastOutput; } bool CassettePort::cassetteIn(EmuTime::param time) { // All analog filtering is ignored for now // only important component is DC-removal // we just assume sample has no DC component short sample; #if COMPONENT_LASERDISC if (!motorControl && laserdiscPlayer) { sample = laserdiscPlayer->readSample(time); } else #endif { sample = getPluggedCasDev().readSample(time); // read 1 sample } bool result = (sample >= 0); // comparator return result; } #if COMPONENT_LASERDISC void CassettePort::setLaserdiscPlayer(LaserdiscPlayer *laserdiscPlayer_) { laserdiscPlayer = laserdiscPlayer_; } #endif void CassettePort::unplug(EmuTime::param time) { Connector::unplug(time); } const string CassettePort::getDescription() const { return "MSX Cassette port"; } string_ref CassettePort::getClass() const { return "Cassette Port"; } CassetteDevice& CassettePort::getPluggedCasDev() const { return *checked_cast(&getPlugged()); } template void CassettePort::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); // don't serialize 'lastOutput', done via MSXPPI } INSTANTIATE_SERIALIZE_METHODS(CassettePort); } // namespace openmsx openmsx-0.10.0/src/cassette/CasImage.hh0000644000175000017500000000161312262345041020441 0ustar manuelmanuel00000000000000#ifndef CASIMAGE_HH #define CASIMAGE_HH #include "CassetteImage.hh" #include "openmsx.hh" #include namespace openmsx { class CliComm; class Filename; class FilePool; /** * Code based on "cas2wav" tool by Vincent van Dam */ class CasImage : public CassetteImage { public: CasImage(const Filename& fileName, FilePool& filePool, CliComm& cliComm); // CassetteImage virtual short getSampleAt(EmuTime::param time); virtual EmuTime getEndTime() const; virtual unsigned getFrequency() const; virtual void fillBuffer(unsigned pos, int** bufs, unsigned num) const; private: void write0(); void write1(); void writeHeader(int s); void writeSilence(int s); void writeByte(byte b); bool writeData(const byte* buf, size_t size, size_t& pos); void convert(const Filename& filename, FilePool& filePool, CliComm& cliComm); std::vector output; }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/CassettePlayerCLI.hh0000644000175000017500000000122412262345041022246 0ustar manuelmanuel00000000000000#ifndef CASSETTEPLAYERCLI_HH #define CASSETTEPLAYERCLI_HH #include "CLIOption.hh" namespace openmsx { class CommandLineParser; class CassettePlayerCLI : public CLIOption, public CLIFileType { public: explicit CassettePlayerCLI(CommandLineParser& commandLineParser); virtual void parseOption(const std::string& option, std::deque& cmdLine); virtual string_ref optionHelp() const; virtual void parseFileType(const std::string& filename, std::deque& cmdLine); virtual string_ref fileTypeHelp() const; private: CommandLineParser& parser; }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/CassetteDevice.hh0000644000175000017500000000115012262345041021657 0ustar manuelmanuel00000000000000#ifndef CASSETTEDEVICE_HH #define CASSETTEDEVICE_HH #include "Pluggable.hh" namespace openmsx { class CassetteDevice : public Pluggable { public: /** * Sets the cassette motor relay * false = off true = on */ virtual void setMotor(bool status, EmuTime::param time) = 0; /** * Read wave data from cassette device */ virtual short readSample(EmuTime::param time) = 0; /** * Sets the cassette output signal * false = low true = high */ virtual void setSignal(bool output, EmuTime::param time) = 0; // Pluggable virtual string_ref getClass() const; }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/WavImage.hh0000644000175000017500000000130212262345041020463 0ustar manuelmanuel00000000000000#ifndef WAVIMAGE_HH #define WAVIMAGE_HH #include "CassetteImage.hh" #include "WavData.hh" #include "DynamicClock.hh" #include "noncopyable.hh" #include namespace openmsx { class Filename; class FilePool; class WavImage : public CassetteImage, private noncopyable { public: explicit WavImage(const Filename& filename, FilePool& filePool); virtual ~WavImage(); virtual short getSampleAt(EmuTime::param time); virtual EmuTime getEndTime() const; virtual unsigned getFrequency() const; virtual void fillBuffer(unsigned pos, int** bufs, unsigned num) const; private: int getSample(unsigned pos) const; WavData wav; DynamicClock clock; short average; }; } // namespace openmsx #endif openmsx-0.10.0/src/cassette/CassetteImage.cc0000644000175000017500000000147612262345041021503 0ustar manuelmanuel00000000000000#include "CassetteImage.hh" #include namespace openmsx { CassetteImage::CassetteImage() : firstFileType(UNKNOWN) { } CassetteImage::~CassetteImage() { } CassetteImage::FileType CassetteImage::getFirstFileType() const { return firstFileType; } void CassetteImage::setFirstFileType(FileType type) { firstFileType = type; } std::string CassetteImage::getFirstFileTypeAsString() const { if (firstFileType == ASCII) { return "ASCII"; } else if (firstFileType == BINARY) { return "binary"; } else if (firstFileType == BASIC) { return "BASIC"; } else { return "unknown"; } } void CassetteImage::setSha1Sum(const Sha1Sum& sha1sum_) { assert(sha1sum.empty()); sha1sum = sha1sum_; } const Sha1Sum& CassetteImage::getSha1Sum() const { assert(!sha1sum.empty()); return sha1sum; } } // namespace openmsx openmsx-0.10.0/src/cassette/CassettePlayer.cc0000644000175000017500000006757112262345041021725 0ustar manuelmanuel00000000000000// TODO: // - improve consistency when a reset occurs: tape is removed when you were // recording, but it is not removed when you were playing // - specify prefix for auto file name generation when recording (setting?) // - append to existing wav files when recording (record command), but this is // basically a special case (pointer at the end) of: // - (partly) overwrite an existing wav file from any given time index // - seek in cassette images for the next and previous file (using empty space?) // - (partly) overwrite existing wav files with new tape data (not very hi prio) // - handle read-only cassette images (e.g.: CAS images or WAV files with a RO // flag): refuse to go to record mode when those are selected // - smartly auto-set the position of tapes: if you insert an existing WAV // file, it will have the position at the start, assuming PLAY mode by // default. When specifiying record mode at insert (somehow), it should be // at the back. // Alternatively, we could remember the index in tape images by storing the // index in some persistent data file with its SHA1 sum as it was as we last // saw it. When there are write actions to the tape, the hash has to be // recalculated and replaced in the data file. An optimization would be to // first simply check on the length of the file and fall back to SHA1 if that // results in multiple matches. #include "CassettePlayer.hh" #include "BooleanSetting.hh" #include "Connector.hh" #include "CassettePort.hh" #include "CommandController.hh" #include "RecordedCommand.hh" #include "DeviceConfig.hh" #include "HardwareConfig.hh" #include "XMLElement.hh" #include "FileContext.hh" #include "FilePool.hh" #include "File.hh" #include "WavImage.hh" #include "CasImage.hh" #include "CliComm.hh" #include "MSXMotherBoard.hh" #include "Reactor.hh" #include "GlobalSettings.hh" #include "CommandException.hh" #include "EventDistributor.hh" #include "FileOperations.hh" #include "WavWriter.hh" #include "ThrottleManager.hh" #include "TclObject.hh" #include "DynamicClock.hh" #include "EmuDuration.hh" #include "StringOp.hh" #include "serialize.hh" #include "unreachable.hh" #include "memory.hh" #include #include using std::unique_ptr; using std::string; using std::vector; namespace openmsx { static const unsigned RECORD_FREQ = 44100; static const double OUTPUT_AMP = 60.0; enum SyncType { END_OF_TAPE, SYNC_AUDIO_EMU }; class TapeCommand : public RecordedCommand { public: TapeCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, CassettePlayer& cassettePlayer); virtual string execute(const vector& tokens, EmuTime::param time); virtual string help(const vector& tokens) const; virtual void tabCompletion(vector& tokens) const; virtual bool needRecord(const vector& tokens) const; private: CassettePlayer& cassettePlayer; }; static XMLElement createXML() { XMLElement xml("cassetteplayer"); xml.addChild(XMLElement("sound")) .addChild(XMLElement("volume", "5000")); return xml; } CassettePlayer::CassettePlayer(const HardwareConfig& hwConf) : ResampledSoundDevice(hwConf.getMotherBoard(), getName(), getDescription(), 1) , Schedulable(hwConf.getMotherBoard().getScheduler()) , tapePos(EmuTime::zero) , prevSyncTime(EmuTime::zero) , audioPos(0) , motherBoard(hwConf.getMotherBoard()) , tapeCommand(make_unique( motherBoard.getCommandController(), motherBoard.getStateChangeDistributor(), motherBoard.getScheduler(), *this)) , loadingIndicator(make_unique( motherBoard.getReactor().getGlobalSettings().getThrottleManager())) , autoRunSetting(make_unique( motherBoard.getCommandController(), "autoruncassettes", "automatically try to run cassettes", true)) , sampcnt(0) , state(STOP) , lastOutput(false) , motor(false), motorControl(true) , syncScheduled(false) { setInputRate(44100); // Initialize with dummy value removeTape(EmuTime::zero); static XMLElement xml = createXML(); registerSound(DeviceConfig(hwConf, xml)); motherBoard.getReactor().getEventDistributor().registerEventListener( OPENMSX_BOOT_EVENT, *this); motherBoard.getMSXCliComm().update(CliComm::HARDWARE, getName(), "add"); } CassettePlayer::~CassettePlayer() { unregisterSound(); if (Connector* connector = getConnector()) { connector->unplug(getCurrentTime()); } motherBoard.getReactor().getEventDistributor().unregisterEventListener( OPENMSX_BOOT_EVENT, *this); motherBoard.getMSXCliComm().update(CliComm::HARDWARE, getName(), "remove"); } void CassettePlayer::autoRun() { if (!playImage) return; // try to automatically run the tape, if that's set CassetteImage::FileType type = playImage->getFirstFileType(); if (!autoRunSetting->getBoolean() || type == CassetteImage::UNKNOWN) { return; } string instr1, instr2; switch (type) { case CassetteImage::ASCII: instr1 = "RUN\\\"CAS:\\\""; break; case CassetteImage::BINARY: instr1 = "BLOAD\\\"CAS:\\\",R"; break; case CassetteImage::BASIC: // Note that CLOAD:RUN won't work: BASIC ignores stuff // after the CLOAD command. That's why it's split in two. instr1 = "CLOAD"; instr2 = "RUN"; break; default: UNREACHABLE; // Shouldn't be possible } string command = "namespace eval ::openmsx {\n" " variable auto_run_bp\n" " proc auto_run_cb {args} {\n" " variable auto_run_bp\n" " debug remove_bp $auto_run_bp\n" " unset auto_run_bp\n" // Without the 0.1s delay here, the type command gets messed up // on MSX1 machines for some reason (starting to type too early?) " after time 0.1 \"type [lindex $args 0]\\\\r\"\n" " set next [lrange $args 1 end]\n" " if {[llength $next] == 0} return\n" // H_READ isn't called after CLOAD, but H_MAIN is. However, it's // also called right after H_READ, so we wait a little before // setting up the breakpoint. " set cmd1 \"openmsx::auto_run_cb $next\"\n" " set cmd2 \"set openmsx::auto_run_bp \\[debug set_bp 0xFF0C 1 \\\"$cmd1\\\"\\]\"\n" // H_MAIN " after time 0.2 $cmd2\n" " }\n" " if {[info exists auto_run_bp]} {debug remove_bp $auto_run_bp\n}\n" " set auto_run_bp [debug set_bp 0xFF07 1 {\n" // H_READ " openmsx::auto_run_cb " + instr1 + " " + instr2 + "\n" " }]\n" // re-trigger hook(s), needed when already booted in BASIC " type \'\\r\n" "}"; try { motherBoard.getCommandController().executeCommand(command); } catch (CommandException& e) { motherBoard.getMSXCliComm().printWarning( "Error executing loading instruction using command \"" + command + "\" for AutoRun: " + e.getMessage() + "\n Please report a bug."); } } CassettePlayer::State CassettePlayer::getState() const { return state; } string CassettePlayer::getStateString() const { switch (getState()) { case PLAY: return "play"; case RECORD: return "record"; case STOP: return "stop"; } UNREACHABLE; return ""; } bool CassettePlayer::isRolling() const { // Is the tape 'rolling'? // is true when: // not in stop mode (there is a tape inserted and not at end-of-tape) // AND [ user forced playing (motorcontrol=off) OR motor enabled by // software (motor=on) ] return (getState() != STOP) && (motor || !motorControl); } double CassettePlayer::getTapePos(EmuTime::param time) { sync(time); return (tapePos - EmuTime::zero).toDouble(); } double CassettePlayer::getTapeLength(EmuTime::param time) { if (playImage) { return (playImage->getEndTime() - EmuTime::zero).toDouble(); } else if (getState() == RECORD) { return getTapePos(time); } else { return 0.0; } } void CassettePlayer::checkInvariants() const { switch (getState()) { case STOP: assert(!recordImage); if (playImage) { // we're at end-of tape assert(!getImageName().empty()); } else { // no tape inserted, imageName may or may not be empty } break; case PLAY: assert(!getImageName().empty()); assert(!recordImage); assert(playImage); break; case RECORD: assert(!getImageName().empty()); assert(recordImage); assert(!playImage); break; default: UNREACHABLE; } } void CassettePlayer::setState(State newState, const Filename& newImage, EmuTime::param time) { sync(time); // set new state if different from old state State oldState = getState(); if (oldState == newState) return; // cannot directly switch from PLAY to RECORD or vice-versa, // (should always go via STOP) assert(!((oldState == PLAY) && (newState == RECORD))); assert(!((oldState == RECORD) && (newState == PLAY))); // stuff for leaving the old state // 'recordImage==nullptr' can happen in case of loadstate. if ((oldState == RECORD) && recordImage) { flushOutput(); bool empty = recordImage->isEmpty(); recordImage.reset(); if (empty) { // delete the created WAV file, as it is useless FileOperations::unlink(getImageName().getResolved()); // ignore errors setImageName(Filename()); } } // actually switch state state = newState; setImageName(newImage); // stuff for entering the new state if (newState == RECORD) { partialOut = 0.0; partialInterval = 0.0; lastX = lastOutput ? OUTPUT_AMP : -OUTPUT_AMP; lastY = 0.0; } motherBoard.getMSXCliComm().update( CliComm::STATUS, "cassetteplayer", getStateString()); updateLoadingState(time); // sets SP for tape-end detection checkInvariants(); } void CassettePlayer::updateLoadingState(EmuTime::param time) { // TODO also set loadingIndicator for RECORD? // note: we don't use isRolling() loadingIndicator->update(motor && (getState() == PLAY)); removeSyncPoint(END_OF_TAPE); if (isRolling() && (getState() == PLAY)) { setSyncPoint(time + (playImage->getEndTime() - tapePos), END_OF_TAPE); } } void CassettePlayer::setImageName(const Filename& newImage) { casImage = newImage; motherBoard.getMSXCliComm().update( CliComm::MEDIA, "cassetteplayer", casImage.getResolved()); } const Filename& CassettePlayer::getImageName() const { return casImage; } void CassettePlayer::insertTape(const Filename& filename) { if (!filename.empty()) { FilePool& filePool = motherBoard.getReactor().getFilePool(); try { // first try WAV playImage = make_unique(filename, filePool); } catch (MSXException& e) { try { // if that fails use CAS playImage = make_unique( filename, filePool, motherBoard.getMSXCliComm()); } catch (MSXException& e2) { throw MSXException( "Failed to insert WAV image: \"" + e.getMessage() + "\" and also failed to insert CAS image: \"" + e2.getMessage() + '\"'); } } } else { // This is a bit tricky, consider this scenario: we switch from // RECORD->PLAY, but we didn't actually record anything: The // removeTape() call above (indirectly) deletes the empty // recorded wav image and also clears imageName. Now because // the 'filename' parameter is passed by reference, and because // getImageName() returns a reference, this 'filename' // parameter now also is an empty string. } // possibly recreate resampler unsigned inputRate = playImage ? playImage->getFrequency() : 44100; if (inputRate != getInputRate()) { setInputRate(inputRate); createResampler(); } setImageName(filename); } void CassettePlayer::playTape(const Filename& filename, EmuTime::param time) { if (getState() == RECORD) { // First close the recorded image. Otherwise it goes wrong // if you switch from RECORD->PLAY on the same image. setState(STOP, getImageName(), time); // keep current image } insertTape(filename); rewind(time); // sets PLAY mode autoRun(); } void CassettePlayer::rewind(EmuTime::param time) { assert(getState() != RECORD); tapePos = EmuTime::zero; audioPos = 0; if (getImageName().empty()) { // no image inserted, do nothing assert(getState() == STOP); } else { // keep current image setState(PLAY, getImageName(), time); } } void CassettePlayer::recordTape(const Filename& filename, EmuTime::param time) { removeTape(time); // flush (possible) previous recording recordImage = make_unique(filename, 1, RECORD_FREQ); tapePos = EmuTime::zero; setState(RECORD, filename, time); } void CassettePlayer::removeTape(EmuTime::param time) { playImage.reset(); tapePos = EmuTime::zero; setState(STOP, Filename(), time); } void CassettePlayer::setMotor(bool status, EmuTime::param time) { if (status != motor) { sync(time); motor = status; updateLoadingState(time); } } void CassettePlayer::setMotorControl(bool status, EmuTime::param time) { if (status != motorControl) { sync(time); motorControl = status; updateLoadingState(time); } } short CassettePlayer::readSample(EmuTime::param time) { if (getState() == PLAY) { // playing sync(time); return isRolling() ? playImage->getSampleAt(tapePos) : 0; } else { // record or stop return 0; } } void CassettePlayer::setSignal(bool output, EmuTime::param time) { sync(time); lastOutput = output; } void CassettePlayer::sync(EmuTime::param time) { EmuDuration duration = time - prevSyncTime; prevSyncTime = time; updateTapePosition(duration, time); generateRecordOutput(duration); } void CassettePlayer::updateTapePosition( EmuDuration::param duration, EmuTime::param time) { if (!isRolling()) return; tapePos += duration; // synchronize audio with actual tape position if ((getState() == PLAY) && !syncScheduled) { // don't sync too often, this improves sound quality syncScheduled = true; setSyncPoint(time + EmuDuration::sec(1), SYNC_AUDIO_EMU); } } void CassettePlayer::generateRecordOutput(EmuDuration::param duration) { if (!recordImage || !isRolling()) return; double out = lastOutput ? OUTPUT_AMP : -OUTPUT_AMP; double samples = duration.toDouble() * RECORD_FREQ; double rest = 1.0 - partialInterval; if (rest <= samples) { // enough to fill next interval partialOut += out * rest; fillBuf(1, int(partialOut)); samples -= rest; // fill complete intervals int count = int(samples); if (count > 0) { fillBuf(count, int(out)); } samples -= count; // partial last interval partialOut = samples * out; partialInterval = 0.0; } else { partialOut += samples * out; partialInterval += samples; } } void CassettePlayer::fillBuf(size_t length, double x) { assert(recordImage); static const double A = 252.0 / 256.0; double y = lastY + (x - lastX); while (length) { size_t len = std::min(length, BUF_SIZE - sampcnt); for (size_t j = 0; j < len; ++j) { buf[sampcnt++] = int(y) + 128; y *= A; } length -= len; assert(sampcnt <= BUF_SIZE); if (BUF_SIZE == sampcnt) { flushOutput(); } } lastY = y; lastX = x; } void CassettePlayer::flushOutput() { try { recordImage->write(buf, 1, unsigned(sampcnt)); sampcnt = 0; recordImage->flush(); // update wav header } catch (MSXException& e) { motherBoard.getMSXCliComm().printWarning( "Failed to write to tape: " + e.getMessage()); } } const string& CassettePlayer::getName() const { static const string name("cassetteplayer"); return name; } string_ref CassettePlayer::getDescription() const { // TODO: this description is not entirely accurate, but it is used // as an identifier for this audio device in e.g. Catapult. We should // use another way to identify audio devices A.S.A.P.! return "Cassetteplayer, use to read .cas or .wav files."; } void CassettePlayer::plugHelper(Connector& connector, EmuTime::param time) { sync(time); lastOutput = static_cast(connector).lastOut(); } void CassettePlayer::unplugHelper(EmuTime::param time) { // note: may not throw exceptions setState(STOP, getImageName(), time); // keep current image } void CassettePlayer::generateChannels(int** buffers, unsigned num) { if ((getState() != PLAY) || !isRolling()) { buffers[0] = nullptr; return; } // Note: fillBuffer() replaces the values in the buffer. It should add // to the existing values in the buffer. But because there is only // one channel this doesn't matter (buffer contains all zeros). playImage->fillBuffer(audioPos, buffers, num); audioPos += num; } int CassettePlayer::signalEvent(const std::shared_ptr& event) { if (event->getType() == OPENMSX_BOOT_EVENT) { if (!getImageName().empty()) { // Reinsert tape to make sure everything is reset. try { playTape(getImageName(), getCurrentTime()); } catch (MSXException& e) { motherBoard.getMSXCliComm().printWarning( "Failed to insert tape: " + e.getMessage()); } } } return 0; } void CassettePlayer::executeUntil(EmuTime::param time, int userData) { switch (userData) { case END_OF_TAPE: // tape ended motherBoard.getMSXCliComm().printWarning( "Tape end reached... stopping. " "You may need to insert another tape image " "that contains side B. (Or you used the wrong " "loading command.)"); setState(STOP, getImageName(), time); // keep current image break; case SYNC_AUDIO_EMU: if (getState() == PLAY) { updateStream(time); sync(time); DynamicClock clk(EmuTime::zero); clk.setFreq(playImage->getFrequency()); audioPos = clk.getTicksTill(tapePos); } syncScheduled = false; break; } } // class TapeCommand TapeCommand::TapeCommand(CommandController& commandController, StateChangeDistributor& stateChangeDistributor, Scheduler& scheduler, CassettePlayer& cassettePlayer_) : RecordedCommand(commandController, stateChangeDistributor, scheduler, "cassetteplayer") , cassettePlayer(cassettePlayer_) { } string TapeCommand::execute(const vector& tokens, EmuTime::param time) { StringOp::Builder result; if (tokens.size() == 1) { Interpreter& interpreter = getInterpreter(); // Returning Tcl lists here, similar to the disk commands in // DiskChanger TclObject tmp(interpreter); tmp.addListElement(getName() + ':'); tmp.addListElement(cassettePlayer.getImageName().getResolved()); TclObject options(interpreter); options.addListElement(cassettePlayer.getStateString()); tmp.addListElement(options); result << tmp.getString(); } else if (tokens[1] == "new") { string directory = "taperecordings"; string prefix = "openmsx"; string extension = ".wav"; string filename = FileOperations::parseCommandFileArgument( (tokens.size() == 3) ? tokens[2] : "", directory, prefix, extension); cassettePlayer.recordTape(Filename(filename), time); result << "Created new cassette image file: " << filename << ", inserted it and set recording mode."; } else if (tokens[1] == "insert" && tokens.size() == 3) { try { result << "Changing tape"; Filename filename(tokens[2], UserFileContext()); cassettePlayer.playTape(filename, time); } catch (MSXException& e) { throw CommandException(e.getMessage()); } } else if (tokens[1] == "motorcontrol" && tokens.size() == 3) { if (tokens[2] == "on") { cassettePlayer.setMotorControl(true, time); result << "Motor control enabled."; } else if (tokens[2] == "off") { cassettePlayer.setMotorControl(false, time); result << "Motor control disabled."; } else { throw SyntaxError(); } } else if (tokens.size() != 2) { throw SyntaxError(); } else if (tokens[1] == "motorcontrol") { result << "Motor control is " << (cassettePlayer.motorControl ? "on" : "off"); } else if (tokens[1] == "record") { result << "TODO: implement this... (sorry)"; } else if (tokens[1] == "play") { if (cassettePlayer.getState() == CassettePlayer::RECORD) { try { result << "Play mode set, rewinding tape."; cassettePlayer.playTape( cassettePlayer.getImageName(), time); } catch (MSXException& e) { throw CommandException(e.getMessage()); } } else if (cassettePlayer.getState() == CassettePlayer::STOP) { throw CommandException("No tape inserted or tape at end!"); } else { // PLAY mode result << "Already in play mode."; } } else if (tokens[1] == "eject") { result << "Tape ejected"; cassettePlayer.removeTape(time); } else if (tokens[1] == "rewind") { if (cassettePlayer.getState() == CassettePlayer::RECORD) { try { result << "First stopping recording... "; cassettePlayer.playTape( cassettePlayer.getImageName(), time); } catch (MSXException& e) { throw CommandException(e.getMessage()); } } cassettePlayer.rewind(time); result << "Tape rewound"; } else if (tokens[1] == "getpos") { result << cassettePlayer.getTapePos(time); } else if (tokens[1] == "getlength") { result << cassettePlayer.getTapeLength(time); } else { try { result << "Changing tape"; Filename filename(tokens[1], UserFileContext()); cassettePlayer.playTape(filename, time); } catch (MSXException& e) { throw CommandException(e.getMessage()); } } //if (!cassettePlayer.getConnector()) { // cassettePlayer.cliComm.printWarning("Cassetteplayer not plugged in."); //} return result; } string TapeCommand::help(const vector& tokens) const { string helptext; if (tokens.size() >= 2) { if (tokens[1] == "eject") { helptext = "Well, just eject the cassette from the cassette " "player/recorder!"; } else if (tokens[1] == "rewind") { helptext = "Indeed, rewind the tape that is currently in the " "cassette player/recorder..."; } else if (tokens[1] == "motorcontrol") { helptext = "Setting this to 'off' is equivalent to " "disconnecting the black remote plug from the " "cassette player: it makes the cassette player " "run (if in play mode); the motor signal from the " "MSX will be ignored. Normally this is set to " "'on': the cassetteplayer obeys the motor control " "signal from the MSX."; } else if (tokens[1] == "play") { helptext = "Go to play mode. Only useful if you were in " "record mode (which is currently the only other " "mode available)."; } else if (tokens[1] == "new") { helptext = "Create a new cassette image. If the file name is " "omitted, one will be generated in the default " "directory for tape recordings. Implies going to " "record mode (why else do you want a new cassette " "image?)."; } else if (tokens[1] == "insert") { helptext = "Inserts the specified cassette image into the " "cassette player, rewinds it and switches to play " "mode."; } else if (tokens[1] == "record") { helptext = "Go to record mode. NOT IMPLEMENTED YET. Will be " "used to be able to resume recording to an " "existing cassette image, previously inserted with " "the insert command."; } else if (tokens[1] == "getpos") { helptext = "Return the position of the tape, in seconds from " "the beginning of the tape."; } else if (tokens[1] == "getlength") { helptext = "Return the length of the tape in seconds."; } } else { helptext = "cassetteplayer eject " ": remove tape from virtual player\n" "cassetteplayer rewind " ": rewind tape in virtual player\n" "cassetteplayer motorcontrol " ": enables or disables motor control (remote)\n" "cassetteplayer play " ": change to play mode (default)\n" "cassetteplayer record " ": change to record mode (NOT IMPLEMENTED YET)\n" "cassetteplayer new [] " ": create and insert new tape image file and go to record mode\n" "cassetteplayer insert " ": insert (a different) tape file\n" "cassetteplayer getpos " ": query the position of the tape\n" "cassetteplayer getlength " ": query the total length of the tape\n" "cassetteplayer " ": insert (a different) tape file\n"; } return helptext; } void TapeCommand::tabCompletion(vector& tokens) const { if (tokens.size() == 2) { static const char* const cmds[] = { "eject", "rewind", "motorcontrol", "insert", "new", "play", "getpos", "getlength", //"record", }; completeFileName(tokens, UserFileContext(), cmds); } else if ((tokens.size() == 3) && (tokens[1] == "insert")) { completeFileName(tokens, UserFileContext()); } else if ((tokens.size() == 3) && (tokens[1] == "motorcontrol")) { static const char* const extra[] = { "on", "off" }; completeString(tokens, extra); } } bool TapeCommand::needRecord(const vector& tokens) const { return tokens.size() > 1; } static enum_string stateInfo[] = { { "PLAY", CassettePlayer::PLAY }, { "RECORD", CassettePlayer::RECORD }, { "STOP", CassettePlayer::STOP } }; SERIALIZE_ENUM(CassettePlayer::State, stateInfo); // version 1: initial version // version 2: added checksum template void CassettePlayer::serialize(Archive& ar, unsigned version) { if (recordImage) { // buf, sampcnt flushOutput(); } ar.serialize("casImage", casImage); Sha1Sum oldChecksum; if (!ar.isLoader() && playImage) { oldChecksum = playImage->getSha1Sum(); } if (ar.versionAtLeast(version, 2)) { string oldChecksumStr = oldChecksum.empty() ? "" : oldChecksum.toString(); ar.serialize("checksum", oldChecksumStr); oldChecksum = oldChecksumStr.empty() ? Sha1Sum() : Sha1Sum(oldChecksumStr); } if (ar.isLoader()) { FilePool& filePool = motherBoard.getReactor().getFilePool(); removeTape(getCurrentTime()); casImage.updateAfterLoadState(); if (!oldChecksum.empty() && !FileOperations::exists(casImage.getResolved())) { if (auto file = filePool.getFile( FilePool::TAPE, oldChecksum)) { casImage.setResolved(file->getURL()); } } try { insertTape(casImage); } catch (MSXException&) { if (oldChecksum.empty()) { // It's OK if we cannot reinsert an empty // image. One likely scenario for this case is // the following: // - cassetteplayer new myfile.wav // - don't actually start saving to tape yet // - create a savestate and load that state // Because myfile.wav contains no data yet, it // is deleted from the filesystem. So on a // loadstate it won't be found. } else { throw; } } if (playImage && !oldChecksum.empty()) { Sha1Sum newChecksum = playImage->getSha1Sum(); if (oldChecksum != newChecksum) { motherBoard.getMSXCliComm().printWarning( "The content of the tape " + casImage.getResolved() + " has changed since the time this " "savestate was created. This might " "result in emulation problems."); } } } // only for RECORD //double lastX; //double lastY; //double partialOut; //double partialInterval; //std::unique_ptr recordImage; ar.serialize("tapePos", tapePos); ar.serialize("prevSyncTime", prevSyncTime); ar.serialize("audioPos", audioPos); ar.serialize("state", state); ar.serialize("lastOutput", lastOutput); ar.serialize("motor", motor); ar.serialize("motorControl", motorControl); if (ar.isLoader()) { if (playImage && (tapePos > playImage->getEndTime())) { tapePos = playImage->getEndTime(); motherBoard.getMSXCliComm().printWarning("Tape position " "beyond tape end! Setting tape position to end. " "This can happen if you load a replay from an " "older openMSX version with a different CAS-to-WAV " "baud rate or when the tape image has been changed " "compared to when the replay was created."); } if (state == RECORD) { // TODO we don't support savestates in RECORD mode yet motherBoard.getMSXCliComm().printWarning( "Restoring a state where the MSX was saving to " "tape is not yet supported. Emulation will " "continue without actually saving."); setState(STOP, getImageName(), getCurrentTime()); } if (!playImage && (state == PLAY)) { // This should only happen for manually edited // savestates, though we shouldn't crash on it. setState(STOP, getImageName(), getCurrentTime()); } updateLoadingState(getCurrentTime()); } } INSTANTIATE_SERIALIZE_METHODS(CassettePlayer); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, CassettePlayer, "CassettePlayer"); } // namespace openmsx openmsx-0.10.0/src/cassette/DummyCassetteDevice.cc0000644000175000017500000000121012262345041022656 0ustar manuelmanuel00000000000000#include "DummyCassetteDevice.hh" namespace openmsx { void DummyCassetteDevice::setMotor(bool /*status*/, EmuTime::param /*time*/) { // do nothing } short DummyCassetteDevice::readSample(EmuTime::param /*time*/) { return 32767; // TODO check value } void DummyCassetteDevice::setSignal(bool /*output*/, EmuTime::param /*time*/) { // do nothing } string_ref DummyCassetteDevice::getDescription() const { return ""; } void DummyCassetteDevice::plugHelper(Connector& /*connector*/, EmuTime::param /*time*/) { } void DummyCassetteDevice::unplugHelper(EmuTime::param /*time*/) { } } // namespace openmsx openmsx-0.10.0/src/PrinterPortLogger.cc0000644000175000017500000000504112262345041020572 0ustar manuelmanuel00000000000000#include "PrinterPortLogger.hh" #include "PlugException.hh" #include "FileException.hh" #include "File.hh" #include "FilenameSetting.hh" #include "serialize.hh" #include "memory.hh" #include namespace openmsx { PrinterPortLogger::PrinterPortLogger(CommandController& commandController) : logFilenameSetting(make_unique( commandController, "printerlogfilename", "filename of the file where the printer output is logged to", "printer.log")) , toPrint(0) // Initialize to avoid a static analysis (cppcheck) warning. // For correctness it's not strictly needed to initialize // this variable. But understanding why exactly it's not // needed depends on the implementation details of a few // other classes, so let's simplify stuff and just // initialize. , prevStrobe(true) { } PrinterPortLogger::~PrinterPortLogger() { } bool PrinterPortLogger::getStatus(EmuTime::param /*time*/) { return false; // false = low = ready } void PrinterPortLogger::setStrobe(bool strobe, EmuTime::param /*time*/) { if (file && !strobe && prevStrobe) { // falling edge file->write(&toPrint, 1); file->flush(); // optimize when it turns out flushing // every time is too slow } prevStrobe = strobe; } void PrinterPortLogger::writeData(byte data, EmuTime::param /*time*/) { toPrint = data; } void PrinterPortLogger::plugHelper( Connector& /*connector*/, EmuTime::param /*time*/) { try { file = make_unique(logFilenameSetting->getString(), File::TRUNCATE); } catch (FileException& e) { throw PlugException("Couldn't plug printer logger: " + e.getMessage()); } } void PrinterPortLogger::unplugHelper(EmuTime::param /*time*/) { file.reset(); } const std::string& PrinterPortLogger::getName() const { static const std::string name("logger"); return name; } string_ref PrinterPortLogger::getDescription() const { return "Log everything that is sent to the printer port to a " "file. The filename can be set with the " "'printerlogfilename' setting."; } template void PrinterPortLogger::serialize(Archive& /*ar*/, unsigned /*version*/) { // We don't try to resume logging to the same file. // And to not accidentally loose a previous log, we don't // overwrite that file automatically. So after savestate/loadstate, // you have to replug the PrinterPortLogger } INSTANTIATE_SERIALIZE_METHODS(PrinterPortLogger); REGISTER_POLYMORPHIC_INITIALIZER(Pluggable, PrinterPortLogger, "PrinterPortLogger"); } // namespace openmsx openmsx-0.10.0/src/MSXResetStatusRegister.cc0000644000175000017500000000222312262345041021524 0ustar manuelmanuel00000000000000#include "MSXResetStatusRegister.hh" #include "serialize.hh" namespace openmsx { MSXResetStatusRegister::MSXResetStatusRegister(const DeviceConfig& config) : MSXDevice(config) , inverted(config.getChildDataAsBool("inverted", false)) { reset(EmuTime::dummy()); } void MSXResetStatusRegister::reset(EmuTime::param /*time*/) { status = inverted ? 0xFF : 0x00; } byte MSXResetStatusRegister::readIO(word port, EmuTime::param time) { return peekIO(port, time); } byte MSXResetStatusRegister::peekIO(word /*port*/, EmuTime::param /*time*/) const { return status; } void MSXResetStatusRegister::writeIO(word /*port*/, byte value, EmuTime::param /*time*/) { if (inverted) { status = value | 0x7F; } else { status = (status & 0x20) | (value & 0xA0); } } template void MSXResetStatusRegister::serialize(Archive& ar, unsigned /*version*/) { ar.template serializeBase(*this); ar.serialize("status", status); } INSTANTIATE_SERIALIZE_METHODS(MSXResetStatusRegister); REGISTER_MSXDEVICE(MSXResetStatusRegister, "F4Device"); // TODO: find a way to handle renames of classes and keep bwcompat with savestates.... } // namespace openmsx openmsx-0.10.0/src/CliExtension.cc0000644000175000017500000000147312262345041017553 0ustar manuelmanuel00000000000000#include "CliExtension.hh" #include "CommandLineParser.hh" #include "MSXMotherBoard.hh" #include "MSXException.hh" #include using std::string; namespace openmsx { CliExtension::CliExtension(CommandLineParser& cmdLineParser_) : cmdLineParser(cmdLineParser_) { cmdLineParser.registerOption("-ext", *this, CommandLineParser::PHASE_EXT); } void CliExtension::parseOption(const string& option, std::deque& cmdLine) { try { string extensionName = getArgument(option, cmdLine); MSXMotherBoard* motherboard = cmdLineParser.getMotherBoard(); assert(motherboard); motherboard->loadExtension(extensionName); } catch (MSXException& e) { throw FatalError(e.getMessage()); } } string_ref CliExtension::optionHelp() const { return "Insert the extension specified in argument"; } } // namespace openmsx openmsx-0.10.0/src/Autofire.cc0000644000175000017500000000205212262345041016717 0ustar manuelmanuel00000000000000#include "Autofire.hh" #include "IntegerSetting.hh" #include "memory.hh" #include #include using std::string; namespace openmsx { Autofire::Autofire(CommandController& commandController, unsigned newMinInts, unsigned newMaxInts, string_ref name) : min_ints(std::max(newMinInts, 1u)) , max_ints(std::max(newMaxInts, min_ints + 1)) , speedSetting(make_unique(commandController, name, "controls the speed of this autofire circuit", 0, 0, 100)) , clock(EmuTime::zero) { setClock(); speedSetting->attach(*this); } Autofire::~Autofire() { speedSetting->detach(*this); } void Autofire::setClock() { int speed = speedSetting->getInt(); clock.setFreq( (2 * 50 * 60) / (max_ints - (speed * (max_ints - min_ints)) / 100)); } void Autofire::update(const Setting& setting) { (void)setting; assert(&setting == speedSetting.get()); setClock(); } bool Autofire::getSignal(EmuTime::param time) { return speedSetting->getInt() == 0 ? false : clock.getTicksTill(time) & 1; } } // namespace openmsx openmsx-0.10.0/src/LedStatus.hh0000644000175000017500000000167512262345041017075 0ustar manuelmanuel00000000000000#ifndef LEDSTATUS_HH #define LEDSTATUS_HH #include "EventListener.hh" #include "noncopyable.hh" #include #include namespace openmsx { class AlarmEvent; class CommandController; class EventDistributor; class MSXCliComm; class ReadOnlySetting; class LedStatus : public EventListener, private noncopyable { public: enum Led { POWER, CAPS, KANA, // same as CODE LED PAUSE, TURBO, FDD, NUM_LEDS // must be last }; explicit LedStatus( EventDistributor& eventDistributor, CommandController& commandController, MSXCliComm& msxCliComm); ~LedStatus(); void setLed(Led led, bool status); private: void handleEvent(Led led); // EventListener virtual int signalEvent(const std::shared_ptr& event); MSXCliComm& msxCliComm; const std::unique_ptr alarm; std::unique_ptr ledStatus[NUM_LEDS]; uint64_t lastTime; bool ledValue[NUM_LEDS]; }; } // namespace openmsx #endif openmsx-0.10.0/src/Pluggable.hh0000644000175000017500000000311312262345041017054 0ustar manuelmanuel00000000000000#ifndef PLUGGABLE_HH #define PLUGGABLE_HH #include "EmuTime.hh" #include "noncopyable.hh" #include "string_ref.hh" namespace openmsx { class Connector; class Pluggable : private noncopyable { public: Pluggable(); virtual ~Pluggable(); /** Name used to identify this pluggable. */ virtual const std::string& getName() const; /** A pluggable belongs to a certain class. A pluggable only fits in * connectors of the same class. */ virtual string_ref getClass() const = 0; /** Description for this pluggable. */ virtual string_ref getDescription() const = 0; /** This method is called when this pluggable is inserted in a * connector. * @throws PlugException */ void plug(Connector& connector, EmuTime::param time); /** This method is called when this pluggable is removed from a * conector. */ void unplug(EmuTime::param time); /** Get the connector this Pluggable is plugged into. Returns nullptr * if this Pluggable is not plugged. */ Connector* getConnector() const; /** Returns true if this pluggable is currently plugged into a connector. * The method getConnector() can also be used, but this is more * descriptive. */ bool isPluggedIn() const { return getConnector() != nullptr; } protected: virtual void plugHelper(Connector& newConnector, EmuTime::param time) = 0; /* implementations of unplugHelper() may not throw exceptions. */ virtual void unplugHelper(EmuTime::param time) = 0; friend class Connector; // for de-serialization void setConnector(Connector* conn); private: Connector* connector; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/0000755000175000017500000000000012262345041015741 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/video/SDLSnow.cc0000644000175000017500000000266612262345041017553 0ustar manuelmanuel00000000000000#include "SDLSnow.hh" #include "OutputSurface.hh" #include "Display.hh" #include "build-info.hh" #include #include namespace openmsx { template SDLSnow::SDLSnow(OutputSurface& output, Display& display_) : Layer(COVER_FULL, Z_BACKGROUND) , display(display_) { // Precalc gray values for noise for (unsigned i = 0; i < 256; ++i) { double t = i / 255.0; gray[i] = output.mapRGB(t, t, t); } } // random routine, less random than libc rand(), but a lot faster static int random() { static int seed = 1; const int IA = 16807; const int IM = 2147483647; const int IQ = 127773; const int IR = 2836; int k = seed / IQ; seed = IA * (seed - k * IQ) - IR * k; if (seed < 0) seed += IM; return seed; } template void SDLSnow::paint(OutputSurface& output) { output.lock(); const unsigned width = output.getWidth(); const unsigned height = output.getHeight(); for (unsigned y = 0; y < height; y += 2) { Pixel* p0 = output.getLinePtrDirect(y + 0); Pixel* p1 = output.getLinePtrDirect(y + 1); for (unsigned x = 0; x < width; x += 2) { unsigned a = random() & 255; p0[x + 0] = p0[x + 1] = gray[a]; } memcpy(p1, p0, width * sizeof(Pixel)); } display.repaintDelayed(100 * 1000); // 10fps } // Force template instantiation. #if HAVE_16BPP template class SDLSnow; #endif #if HAVE_32BPP template class SDLSnow; #endif } // namespace openmsx openmsx-0.10.0/src/video/DummyVideoSystem.hh0000644000175000017500000000107412262345041021553 0ustar manuelmanuel00000000000000#ifndef DUMMYVIDEOSYSTEM_HH #define DUMMYVIDEOSYSTEM_HH #include "VideoSystem.hh" #include "components.hh" namespace openmsx { class DummyVideoSystem : public VideoSystem { public: // VideoSystem interface: virtual std::unique_ptr createRasterizer(VDP& vdp); virtual std::unique_ptr createV9990Rasterizer( V9990& vdp); #if COMPONENT_LASERDISC virtual std::unique_ptr createLDRasterizer( LaserdiscPlayer& ld); #endif virtual void flush(); virtual OutputSurface* getOutputSurface(); }; } // namespace openmsx #endif openmsx-0.10.0/src/video/SDLVideoSystem.hh0000644000175000017500000000332712262345041021105 0ustar manuelmanuel00000000000000#ifndef SDLVIDEOSYSTEM_HH #define SDLVIDEOSYSTEM_HH #include "VideoSystem.hh" #include "EventListener.hh" #include "Observer.hh" #include "noncopyable.hh" #include "components.hh" #include namespace openmsx { class Reactor; class CommandConsole; class Display; class RenderSettings; class VisibleSurface; class Layer; class Setting; class SDLVideoSystem : public VideoSystem, private EventListener, private Observer, private noncopyable { public: /** Activates this video system. * @throw InitException If initialisation fails. */ explicit SDLVideoSystem(Reactor& reactor, CommandConsole& console); /** Deactivates this video system. */ virtual ~SDLVideoSystem(); // VideoSystem interface: virtual std::unique_ptr createRasterizer(VDP& vdp); virtual std::unique_ptr createV9990Rasterizer( V9990& vdp); #if COMPONENT_LASERDISC virtual std::unique_ptr createLDRasterizer( LaserdiscPlayer& ld); #endif virtual bool checkSettings(); virtual void flush(); virtual void takeScreenShot(const std::string& filename, bool withOsd); virtual void setWindowTitle(const std::string& title); virtual OutputSurface* getOutputSurface(); private: // EventListener virtual int signalEvent(const std::shared_ptr& event); // Observer void update(const Setting& subject); void getWindowSize(unsigned& width, unsigned& height); void resize(); Reactor& reactor; Display& display; RenderSettings& renderSettings; std::unique_ptr screen; std::unique_ptr consoleLayer; std::unique_ptr snowLayer; std::unique_ptr iconLayer; std::unique_ptr osdGuiLayer; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/DoubledFrame.hh0000644000175000017500000000134412262345041020615 0ustar manuelmanuel00000000000000#ifndef DOUBLEDFRAME_HH #define DOUBLEDFRAME_HH #include "FrameSource.hh" namespace openmsx { /** Produces a video frame that has every line from the input frame twice * plus a number of black lines at the top. * This class does not copy the data from the input FrameSource. */ class DoubledFrame : public FrameSource { public: explicit DoubledFrame(const SDL_PixelFormat& format); void init(FrameSource* field, unsigned skip); private: virtual unsigned getLineWidth(unsigned line) const; virtual const void* getLineInfo( unsigned line, unsigned& width, void* buf, unsigned bufWidth) const; /** The original frame whose data will be doubled. */ FrameSource* field; unsigned skip; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/RenderSettings.hh0000644000175000017500000001627312262345041021233 0ustar manuelmanuel00000000000000#ifndef RENDERSETTINGS_HH #define RENDERSETTINGS_HH #include "RendererFactory.hh" #include "Observer.hh" #include "noncopyable.hh" #include #include namespace openmsx { class CommandController; class Setting; class IntegerSetting; class FloatSetting; class BooleanSetting; class StringSetting; class VideoSourceSetting; class TclObject; /** Class containing all settings for renderers. * Keeping the settings here makes sure they are preserved when the user * switches to another renderer. */ class RenderSettings : private Observer, private noncopyable { public: /** Render accuracy: granularity of the rendered area. */ enum Accuracy { ACC_SCREEN, ACC_LINE, ACC_PIXEL }; /** Scaler algorithm */ enum ScaleAlgorithm { SCALER_SIMPLE, SCALER_SAI, SCALER_SCALE, SCALER_HQ, SCALER_HQLITE, SCALER_RGBTRIPLET, SCALER_TV, SCALER_MLAA }; enum DisplayDeform { DEFORM_NORMAL, DEFORM_3D }; explicit RenderSettings(CommandController& commandController); ~RenderSettings(); /** Accuracy [screen, line, pixel]. */ EnumSetting& getAccuracy() const { return *accuracySetting; } /** Deinterlacing [on, off]. */ BooleanSetting& getDeinterlace() const { return *deinterlaceSetting; } /** The current max frameskip. */ IntegerSetting& getMaxFrameSkip() const { return *maxFrameSkipSetting; } /** The current min frameskip. */ IntegerSetting& getMinFrameSkip() const { return *minFrameSkipSetting; } /** Full screen [on, off]. */ BooleanSetting& getFullScreen() const { return *fullScreenSetting; } /** The amount of gamma correction. */ FloatSetting& getGamma() const { return *gammaSetting; } /** Brightness video setting. */ FloatSetting& getBrightness() const { return *brightnessSetting; } /** Contrast video setting. */ FloatSetting& getContrast() const { return *contrastSetting; } /** Color matrix setting. */ StringSetting& getColorMatrix() const { return *colorMatrixSetting; } /** Returns true iff the current color matrix is the identity matrix. */ bool isColorMatrixIdentity() const { return cmIdentity; } /** The amount of glow [0..100]. */ IntegerSetting& getGlow() const { return *glowSetting; } /** The amount of noise to add to the frame. */ FloatSetting& getNoise() const { return *noiseSetting; } /** The amount of horizontal blur [0..256]. */ int getBlurFactor() const; /** The alpha value [0..255] of the gap between scanlines. */ int getScanlineFactor() const; /** The amount of space [0..1] between scanlines. */ float getScanlineGap() const; /** The current renderer. */ RendererFactory::RendererSetting& getRenderer() const { return *rendererSetting; } /** The current scaling algorithm. */ EnumSetting& getScaleAlgorithm() const { return *scaleAlgorithmSetting; } /** The current scaling factor. */ IntegerSetting& getScaleFactor() const { return *scaleFactorSetting; } /** Limit number of sprites per line? * If true, limit number of sprites per line as real VDP does. * If false, display all sprites. * For accurate emulation, this setting should be on. * Turning it off can improve games with a lot of flashing sprites, * such as Aleste. */ BooleanSetting& getLimitSprites() const { return *limitSpritesSetting; } /** Disable sprite rendering? */ BooleanSetting& getDisableSprites() const { return *disableSpritesSetting; } /** CmdTiming [real, broken]. * This setting is intended for debugging only, not for users. */ EnumSetting& getCmdTiming() const { return *cmdTimingSetting; } /** TooFastAccess [real, ignored]. * Indicates whether too fast VDP VRAM access should be correctly * emulated (= some accesses are dropped) or ignored (= all accesses * are correctly executed). */ EnumSetting& getTooFastAccess() const { return *tooFastAccessSetting; } /** Display deformation (normal, 3d) * ATM this only works when using the SDLGL-PP renderer. */ EnumSetting& getDisplayDeform() const { return *displayDeformSetting; } /** Amount of horizontal stretch. * This number represents the amount of MSX pixels (normal width) that * will be stretched to the complete width of the host window. * ATM this setting only has effect when using the SDLGL-PP renderer. */ FloatSetting& getHorizontalStretch() const { return *horizontalStretchSetting; } /** The amount of time until the pointer is hidden in the openMSX * window. negative means: no hiding, 0 means immediately. */ FloatSetting& getPointerHideDelay() const { return *pointerHideDelaySetting; } /** Is black frame interleaving enabled? */ BooleanSetting& getInterleaveBlackFrame() const { return *interleaveBlackFrameSetting; } /** Apply brightness, contrast and gamma transformation on the input * color component. The component is expected to be in the range * [0.0 .. 1.0] but it's not an error if it lays outside of this range. * The return value is guaranteed to lay inside this range. * This method skips the cross-influence of color components on each * other that is controlled by the "color_matrix" setting. */ double transformComponent(double c) const; /** Apply brightness, contrast and gamma transformation on the input * color. The R, G and B component are expected to be in the range * [0.0 .. 1.0] but it's not an error if a component lays outside of * this range. After transformation it's guaranteed all components * lay inside this range. */ void transformRGB(double& r, double& g, double& b) const; private: // Observer: void update(const Setting&); /** Sets the "brightness" and "contrast" fields according to the setting * values. */ void updateBrightnessAndContrast(); void parseColorMatrix(const TclObject& value); std::unique_ptr> accuracySetting; std::unique_ptr> cmdTimingSetting; std::unique_ptr> tooFastAccessSetting; std::unique_ptr deinterlaceSetting; std::unique_ptr fullScreenSetting; std::unique_ptr gammaSetting; std::unique_ptr brightnessSetting; std::unique_ptr contrastSetting; std::unique_ptr colorMatrixSetting; std::unique_ptr glowSetting; std::unique_ptr noiseSetting; std::unique_ptr horizontalBlurSetting; std::unique_ptr limitSpritesSetting; std::unique_ptr disableSpritesSetting; std::unique_ptr maxFrameSkipSetting; std::unique_ptr minFrameSkipSetting; std::unique_ptr rendererSetting; std::unique_ptr> scaleAlgorithmSetting; std::unique_ptr scaleFactorSetting; std::unique_ptr scanlineAlphaSetting; std::unique_ptr> displayDeformSetting; std::unique_ptr horizontalStretchSetting; std::unique_ptr pointerHideDelaySetting; std::unique_ptr interleaveBlackFrameSetting; double brightness; double contrast; /** Parsed color matrix, kept in sync with colorMatrix setting. */ double cm[3][3]; /** True iff color matrix is identity matrix. */ bool cmIdentity; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/SuperImposedFrame.cc0000644000175000017500000000531712262345041021650 0ustar manuelmanuel00000000000000#include "SuperImposedFrame.hh" #include "PixelOperations.hh" #include "LineScalers.hh" #include "memory.hh" #include "unreachable.hh" #include "vla.hh" #include "build-info.hh" #include #include namespace openmsx { template class SuperImposedFrameImpl : public SuperImposedFrame { public: SuperImposedFrameImpl(const SDL_PixelFormat& format); private: virtual unsigned getLineWidth(unsigned line) const; virtual const void* getLineInfo( unsigned line, unsigned& width, void* buf, unsigned bufWidth) const; PixelOperations pixelOps; }; // class SuperImposedFrame std::unique_ptr SuperImposedFrame::create( const SDL_PixelFormat& format) { #if HAVE_16BPP if (format.BitsPerPixel == 15 || format.BitsPerPixel == 16) { return make_unique>(format); } #endif #if HAVE_32BPP if (format.BitsPerPixel == 32) { return make_unique>(format); } #endif UNREACHABLE; return nullptr; // avoid warning } SuperImposedFrame::SuperImposedFrame(const SDL_PixelFormat& format) : FrameSource(format) { } void SuperImposedFrame::init( const FrameSource* top_, const FrameSource* bottom_) { top = top_; bottom = bottom_; setHeight(std::max(top->getHeight(), bottom->getHeight())); } // class SuperImposedFrameImpl template SuperImposedFrameImpl::SuperImposedFrameImpl( const SDL_PixelFormat& format) : SuperImposedFrame(format) , pixelOps(format) { } template unsigned SuperImposedFrameImpl::getLineWidth(unsigned line) const { unsigned tNum = (getHeight() == top ->getHeight()) ? line : line / 2; unsigned bNum = (getHeight() == bottom->getHeight()) ? line : line / 2; unsigned tWidth = top ->getLineWidth(tNum); unsigned bWidth = bottom->getLineWidth(bNum); return std::max(tWidth, bWidth); } template const void* SuperImposedFrameImpl::getLineInfo( unsigned line, unsigned& width, void* tBuf_, unsigned bufWidth) const { unsigned tNum = (getHeight() == top ->getHeight()) ? line : line / 2; unsigned bNum = (getHeight() == bottom->getHeight()) ? line : line / 2; unsigned tWidth = top ->getLineWidth(tNum); unsigned bWidth = bottom->getLineWidth(bNum); width = std::max(tWidth, bWidth); // as wide as the widest source width = std::min(width, bufWidth); // but no wider than the output buffer auto* tBuf = static_cast(tBuf_); VLA_SSE_ALIGNED(Pixel, bBuf, width); auto* tLine = top ->getLinePtr(tNum, width, tBuf); auto* bLine = bottom->getLinePtr(bNum, width, bBuf); AlphaBlendLines blend(pixelOps); blend(tLine, bLine, tBuf, width); // possibly tLine == tBuf return tBuf; } } // namespace openmsx openmsx-0.10.0/src/video/VDPVRAM.hh0000644000175000017500000005151112262345041017404 0ustar manuelmanuel00000000000000#ifndef VDPVRAM_HH #define VDPVRAM_HH #include "VRAMObserver.hh" #include "VDP.hh" #include "VDPCmdEngine.hh" #include "DisplayMode.hh" #include "Ram.hh" #include "Math.hh" #include "openmsx.hh" #include "noncopyable.hh" #include "likely.hh" #include namespace openmsx { class SpriteChecker; class Renderer; class LogicalVRAMDebuggable; class PhysicalVRAMDebuggable; /* Note: The way VRAM is accessed depends a lot on who is doing the accessing. For example, the ranges: - Table access is done using masks. - Command engine work areas are rectangles. - CPU access always spans full memory. Maybe define an interface with multiple subclasses? Or is that too much of a performance hit? If accessed through the interface, a virtual method call is made. But invoking the objects directly should be inlined. Timing: Each window reflects the state of the VRAM at a specified moment in time. Because the CPU has full-range write access, it is incorrect for any window to be ahead in time compared to the CPU. Because multi-cycle operations are implemented as atomic, it is currently possible that a window which starts an operation slightly before CPU time ends up slightly after CPU time. Solutions: - break up operations in 1-cycle suboperations (very hard to reverse engineer accurately) - do not start an operation until its end time is after CPU time (requires minor rewrite of command engine) - make the code that uses the timestamps resilient to after-CPU times (current implementation; investigate if this is correct) Window ranges are not at fixed. But they can only be changed by the CPU, so they are fixed until CPU time, which subsystems will never go beyond anyway. The only two subsystems with write access are CPU and command engine. The command engine can only start executing a new command if instructed so by the CPU. Therefore it is known which area the command engine can write in until CPU time: - empty, if the command engine is not executing a command - the command's reach, if the command engine is executing a command Currently the command's reach is not computed: full VRAM is used. Taking the Y coordinate into account would speed things up a lot, because usually commands execute on invisible pages, so the number of area overlaps between renderer and command engine would be reduced significantly. Also sprite tables are usually not written by commands. Reading through a window is done as follows: A subsystem reads the VRAM because it is updating itself to a certain moment in time T. 1. the subsystems syncs the window to T 2. VDPVRAM checks overlap of the window with the command write area no overlap -> go to step 6 3. VDPVRAM syncs the command engine to T 4. the command engine calls VDPVRAM to write each byte it changes in VRAM, call the times this happens C1, C2, C3... 5. at the n-th write, VDPVRAM updates any subsystem with the written address in its window to Cn, this can include the original subsystem 6. the window has reached T now the subsystem can update itself to T Using this approach instead of syncing on read makes sure there is no re-entrance on the subsystem update methods. Note: command engine reads through write window when doing logic-ops. So "source window" and "destination window" would be better names. Interesting observation: Each window is at the same moment in time as the command engine (C): - if a window doesn't overlap with the command destination window, it is stable from a moment before C until the CPU time T - if a window overlaps with the command destination window, it cannot be before C (incorrect) or after C (uncertainty) Since there is only one time for the entire VRAM, the VRAM itself can be said to be at C. This is a justification for having the sync method in VDPVRAM instead of in Window. Writing through a window is done as follows: - CPU write: sync with all non-CPU windows, including command engine write - command engine write: sync with non-CPU and non-command engine windows Syncing with a window is only necessary if the write falls into that window. If all non-CPU windows are disjunct, then all subsystems function independently (at least until CPU time), no need for syncs. So what is interesting, is which windows overlap. Since windows change position infrequently, it may be beneficial to precalculate overlaps. Not necessarily though, because even if two windows overlap, a single write may not be inside the other window. So precalculated overlaps only speeds up in the case there is no overlap. Maybe it's not necessary to know exactly which windows overlap with cmdwrite, only to know whether there are any. If not, sync can be skipped. Is it possible to read multiple bytes at the same time? In other words, get a pointer to an array instead of reading single bytes. Yes, but only the first 64 bytes are guaranteed to be correct, because that is the granularity of the color table. But since whatever is reading the VRAM knows what it is operating on, it can decide for itself how many bytes to read. */ class DummyVRAMOBserver : public VRAMObserver { public: virtual void updateVRAM(unsigned /*offset*/, EmuTime::param /*time*/) {} virtual void updateWindow(bool /*enabled*/, EmuTime::param /*time*/) {} }; /** Specifies an address range in the VRAM. * A VDP subsystem can use this to put a claim on a certain area. * For example, the owner of a read window will be notified before * writes to the corresponding area are commited. * The address range is specified by a mask and is not necessarily * continuous. See "doc/vram-addressing.txt" for details. * TODO: Rename to "Table"? That's the term the VDP data book uses. * Maybe have two classes: "Table" for tables, using a mask, * and "Window" for the command engine, using an interval. */ class VRAMWindow : private noncopyable { private: inline bool isEnabled() const { return baseAddr != -1; } public: /** Gets the mask for this window. * Should only be called if the window is enabled. * TODO: Only used by dirty checking. Maybe a new dirty checking * approach can obsolete this method? */ inline int getMask() const { assert(isEnabled()); return effectiveBaseMask; } /** Sets the mask and enables this window. * @param newBaseMask The table base register, * with the unused bits all ones. * @param newIndexMask The table index mask, * with the unused bits all ones. * @param time The moment in emulated time this change occurs. * TODO: In planar mode, the index bits are rotated one to the right. * Solution: have the caller pass index mask instead of the * number of bits. * For many tables the number of index bits depends on the * display mode anyway. */ inline void setMask(int newBaseMask, int newIndexMask, EmuTime::param time) { origBaseMask = newBaseMask; newBaseMask &= sizeMask; if (isEnabled() && (newBaseMask == effectiveBaseMask) && (newIndexMask == indexMask)) { return; } observer->updateWindow(true, time); effectiveBaseMask = newBaseMask; indexMask = newIndexMask; baseAddr = effectiveBaseMask & indexMask; // this enables window combiMask = ~effectiveBaseMask | indexMask; } /** Disable this window: no address will be considered inside. * @param time The moment in emulated time this change occurs. */ inline void disable(EmuTime::param time) { observer->updateWindow(false, time); baseAddr = -1; } /** Gets a pointer to a contiguous part of the VRAM. The region is * [index, index + size) inside the current window. * @param index Index in table * @param size Size of the block. This is only used to assert that * requested block is not too large. */ inline const byte* getReadArea(unsigned index, unsigned size) const { unsigned endIndex = index + size - 1; unsigned areaBits = Math::floodRight(index ^ endIndex); (void)areaBits; assert((areaBits & effectiveBaseMask) == areaBits); assert((areaBits & ~indexMask) == areaBits); assert(isEnabled()); return &data[effectiveBaseMask & (indexMask | index)]; } /** Similar to getReadArea(), but now with planar addressing mode. * This means the region is split in two: one region for the even bytes * (ptr0) and another for the odd bytes (ptr1). * @param index Index in table * @param size Size of the block. This is only used to assert that * requested block is not too large. * @param ptr0 out Pointer to the block of even numbered bytes. * @param ptr1 out Pointer to the block of odd numbered bytes. */ inline void getReadAreaPlanar( unsigned index, unsigned size, const byte*& ptr0, const byte*& ptr1) const { assert((index & 1) == 0); assert((size & 1) == 0); unsigned endIndex = index + size - 1; unsigned areaBits = Math::floodRight(index ^ endIndex); areaBits = ((areaBits << 16) | (areaBits >> 1)) & 0x1FFFF; (void)areaBits; assert((areaBits & effectiveBaseMask) == areaBits); assert((areaBits & ~indexMask) == areaBits); assert(isEnabled()); unsigned addr = effectiveBaseMask & (indexMask | (index >> 1)); ptr0 = &data[addr | 0x00000]; ptr1 = &data[addr | 0x10000]; } /** Reads a byte from VRAM in its current state. * @param index Index in table, with unused bits set to 1. */ inline byte readNP(unsigned index) const { assert(isEnabled()); return data[effectiveBaseMask & index]; } /** Similar to readNP, but now with planar addressing. * @param index Index in table, with unused bits set to 1. */ inline byte readPlanar(unsigned index) const { assert(isEnabled()); index = ((index & 1) << 16) | ((index & 0x1FFFE) >> 1); unsigned addr = effectiveBaseMask & index; return data[addr]; } /** Is there an observer registered for this window? */ inline bool hasObserver() const { return observer != &dummyObserver; } /** Register an observer on this VRAM window. * It will be called when changes occur within the window. * There can be only one observer per window at any given time. * @param observer The observer to register. */ inline void setObserver(VRAMObserver* observer) { this->observer = observer; } /** Unregister the observer of this VRAM window. */ inline void resetObserver() { observer = &dummyObserver; } /** Test whether an address is inside this window. * "Inside" is defined as: there is at least one index in this window, * which is mapped to the given address. * TODO: Might be replaced by notify(). * @param address The address to test. * @return true iff the address is inside this window. */ inline bool isInside(unsigned address) const { return (address & combiMask) == unsigned(baseAddr); } /** Notifies the observer of this window of a VRAM change, * if the changes address is inside this window. * @param address The address to test. * @param time The moment in emulated time the change occurs. */ inline void notify(unsigned address, EmuTime::param time) { if (isInside(address)) { observer->updateVRAM(address - baseAddr, time); } } /** Inform VRAMWindow of changed sizeMask. * For the moment this only happens when switching the VR bit in VDP * register 8 (in VR=0 mode only 32kB VRAM is addressable). */ void setSizeMask(unsigned newSizeMask, EmuTime::param time) { sizeMask = newSizeMask; if (isEnabled()) { setMask(origBaseMask, indexMask, time); } } template void serialize(Archive& ar, unsigned version); private: /** Only VDPVRAM may construct VRAMWindow objects. */ friend class VDPVRAM; /** Create a new window. * Initially, the window is disabled; use setRange to enable it. */ explicit VRAMWindow(Ram& vram); /** Pointer to the entire VRAM data. */ byte* data; /** Observer associated with this VRAM window. * It will be called when changes occur within the window. * If there is no observer, this variable is nullptr. */ VRAMObserver* observer; /** Base mask as passed to the setMask() method. */ int origBaseMask; /** Effective mask of this window. * This is always equal to 'origBaseMask & sizeMask'. */ int effectiveBaseMask; /** Index mask of this window. */ int indexMask; /** Lowest address in this window. * Or -1 when this window is disabled. */ int baseAddr; /** Combination of effectiveBaseMask and index mask used for "inside" checks. */ int combiMask; /** Mask to handle vram mirroring * Note: this only handles mirroring for power-of-2 sizes * mirroring of extended VRAM is handled in a different way */ int sizeMask; static DummyVRAMOBserver dummyObserver; }; /** Manages VRAM contents and synchronises the various users of the VRAM. * VDPVRAM does not apply planar remapping to addresses, this is the * responsibility of the caller. */ class VDPVRAM : private noncopyable { public: VDPVRAM(VDP& vdp, unsigned size, EmuTime::param time); ~VDPVRAM(); /** Initialize VRAM content to power-up state. */ void clear(); /** Update VRAM state to specified moment in time. * @param time Moment in emulated time to update VRAM to. * TODO: Replace this method by VRAMWindow::sync(). */ inline void sync(EmuTime::param time) { assert(vdp.isInsideFrame(time)); cmdEngine->sync(time); } /** Write a byte from the command engine. * Synchronisation with reads by the command engine is skipped. * TODO: Replace by "cmdSync ; VRAMWindow::write". * Note: "cmdSync", because it checks against read windows, unlike * the other sync which checks against the cmd write window. */ inline void cmdWrite(unsigned address, byte value, EmuTime::param time) { #ifdef DEBUG // Rewriting history is not allowed. assert(time >= vramTime); #endif assert(vdp.isInsideFrame(time)); // handle mirroring and non-present ram chips address &= sizeMask; if (unlikely(address >= actualSize)) { // 192kb vram is mirroring is handled elsewhere assert(address < 0x30000); // only happens in case of 16kb vram while you write // to range [0x4000,0x8000) return; } writeCommon(address, value, time); } /** Write a byte to VRAM through the CPU interface. * @param address The address to write. * @param value The value to write. * @param time The moment in emulated time this write occurs. */ inline void cpuWrite(unsigned address, byte value, EmuTime::param time) { #ifdef DEBUG // Rewriting history is not allowed. assert(time >= vramTime); #endif assert(vdp.isInsideFrame(time)); // handle mirroring and non-present ram chips address &= sizeMask; if (unlikely(address >= actualSize)) { // 192kb vram is mirroring is handled elsewhere assert(address < 0x30000); // only happens in case of 16kb vram while you write // to range [0x4000,0x8000) return; } // We should still sync with cmdEngine, even if the VRAM already // contains the value we're about to write (e.g. it's possible // syncing with cmdEngine changes that value, and this write // restores it again). This fixes bug: // [2844043] Hinotori - Firebird small graphics corruption if (cmdReadWindow .isInside(address) || cmdWriteWindow.isInside(address)) { cmdEngine->sync(time); } writeCommon(address, value, time); } /** Read a byte from VRAM though the CPU interface. * @param address The address to read. * @param time The moment in emulated time this read occurs. * @return The VRAM contents at the specified address. */ inline byte cpuRead(unsigned address, EmuTime::param time) { #ifdef DEBUG // VRAM should never get ahead of CPU. assert(time >= vramTime); #endif assert(vdp.isInsideFrame(time)); address &= sizeMask; if (cmdWriteWindow.isInside(address)) { cmdEngine->sync(time); } return data[address]; } /** Used by the VDP to signal display mode changes. * VDPVRAM will inform the Renderer, command engine and the sprite * checker of this change. * TODO: Does this belong in VDPVRAM? * @param mode The new display mode. * @param time The moment in emulated time this change occurs. */ void updateDisplayMode(DisplayMode mode, EmuTime::param time); /** Used by the VDP to signal display enabled changes. * Both the regular border start/end and forced blanking by clearing * the display enable bit are considered display enabled changes. * @param enabled The new display enabled state. * @param time The moment in emulated time this change occurs. */ void updateDisplayEnabled(bool enabled, EmuTime::param time); /** Used by the VDP to signal sprites enabled changes. * @param enabled The new sprites enabled state. * @param time The moment in emulated time this change occurs. */ void updateSpritesEnabled(bool enabled, EmuTime::param time); /** Change between VR=0 and VR=1 mode. * @param mode false->VR=0 true->VR=1 * @param time The moment in emulated time this change occurs. */ void updateVRMode(bool mode, EmuTime::param time); void setRenderer(Renderer* renderer, EmuTime::param time); /** Returns the size of VRAM in bytes */ unsigned getSize() const { return actualSize; } /** Necessary because of circular dependencies. */ inline void setSpriteChecker(SpriteChecker* spriteChecker) { this->spriteChecker = spriteChecker; } /** Necessary because of circular dependencies. */ inline void setCmdEngine(VDPCmdEngine* cmdEngine) { this->cmdEngine = cmdEngine; } /** TMS99x8 VRAM can be mapped in two ways. * See implementation for more details. */ void change4k8kMapping(bool mapping8k); template void serialize(Archive& ar, unsigned version); private: /* Common code of cmdWrite() and cpuWrite() */ inline void writeCommon(unsigned address, byte value, EmuTime::param time) { // Check that VRAM will actually be changed. // A lot of costly syncs can be saved if the same value is written. // For example Penguin Adventure always uploads the whole frame, // even if it is the same as the previous frame. if (data[address] == value) return; // Subsystem synchronisation should happen before the commit, // to be able to draw backlog using old state. bitmapVisibleWindow.notify(address, time); spriteAttribTable.notify(address, time); spritePatternTable.notify(address, time); data[address] = value; #ifdef DEBUG vramTime = time; #endif // Cache dirty marking should happen after the commit, // otherwise the cache could be re-validated based on old state. // these two seem to be unused // bitmapCacheWindow.notify(address, time); // nameTable.notify(address, time); assert(!bitmapCacheWindow.hasObserver()); assert(!nameTable.hasObserver()); // in the past GLRasterizer observed these two, now there are none assert(!colorTable.hasObserver()); assert(!patternTable.hasObserver()); /* TODO: There seems to be a significant difference between subsystem sync and cache admin. One example is the code above, the other is updateWindow, where subsystem sync is interested in windows that were enabled before (new state doesn't matter), while cache admin is interested in windows that become enabled (old state doesn't matter). Does this mean it makes sense to have separate VRAMWindow like classes for each category? Note: In the future, sprites may switch category, or fall in both. */ } void setSizeMask(EmuTime::param time); /** VDP this VRAM belongs to. */ VDP& vdp; /** VRAM data block. */ Ram data; /** Debuggable with mode dependend view on the vram * Screen7/8 are not interleaved in this mode. * This debuggable is also at least 128kB in size (it possibly * contains unmapped regions). */ const std::unique_ptr logicalVRAMDebug; /** Physical view on the VRAM. * Screen 7/8 are interleaved in this mode. The size of this * debuggable is the same as the actual VRAM size. */ const std::unique_ptr physicalVRAMDebug; // TODO: Renderer field can be removed, if updateDisplayMode // and updateDisplayEnabled are moved back to VDP. // Is that a good idea? Renderer* renderer; VDPCmdEngine* cmdEngine; SpriteChecker* spriteChecker; /** Current time: the moment up until when the VRAM is updated. * Note: This is only used for debugging. */ #ifdef DEBUG EmuTime vramTime; #endif /** Mask to handle vram mirroring * Note: this only handles mirroring at power-of-2 sizes * mirroring of extended VRAM is handled in a different way */ unsigned sizeMask; /** Actual size of VRAM. Normally this is in sync with sizeMask, but * for 16kb VRAM sizeMask is 32kb-1 while actualSize is only 16kb. */ const unsigned actualSize; /** Corresponds to the VR bit (bit 3 in VDP register 8). */ bool vrMode; public: VRAMWindow cmdReadWindow; VRAMWindow cmdWriteWindow; VRAMWindow nameTable; VRAMWindow colorTable; VRAMWindow patternTable; VRAMWindow bitmapVisibleWindow; VRAMWindow bitmapCacheWindow; VRAMWindow spriteAttribTable; VRAMWindow spritePatternTable; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/PostProcessor.hh0000644000175000017500000001221712262345041021112 0ustar manuelmanuel00000000000000#ifndef POSTPROCESSOR_HH #define POSTPROCESSOR_HH #include "FrameSource.hh" #include "VideoLayer.hh" #include "Schedulable.hh" #include "EmuTime.hh" #include #include namespace openmsx { class Display; class RenderSettings; class RawFrame; class DeinterlacedFrame; class DoubledFrame; class SuperImposedFrame; class AviRecorder; class CliComm; class EventDistributor; /** Abstract base class for post processors. * A post processor builds the frame that is displayed from the MSX frame, * while applying effects such as scalers, noise etc. * TODO: With some refactoring, it would be possible to move much or even all * of the post processing code here instead of in the subclasses. */ class PostProcessor : public VideoLayer, private Schedulable { public: virtual ~PostProcessor(); // Layer interface: virtual void paint(OutputSurface& output) = 0; /** Sets up the "abcdFrame" variables for a new frame. * TODO: The point of passing the finished frame in and the new workFrame * out is to be able to split off the scaler application as a * separate class. * @param finishedFrame Frame that has just become available. * @param field Specifies what role (if any) the new frame plays in * interlacing. * @param time The moment in time the frame becomes available. Used to * calculate the framerate for recording (depends on * PAL/NTSC, frameskip). * @return RawFrame object that can be used for building the next frame. */ virtual std::unique_ptr rotateFrames( std::unique_ptr finishedFrame, FrameSource::FieldType field, EmuTime::param time); /** Set the Video frame on which to superimpose the 'normal' output of * this PostProcessor. Superimpose is done (preferably) after the * normal output is scaled. IOW the video frame is (preferably) left * unchanged, though exceptions are e.g. scalers that render * scanlines, those are preferably also visible in the video frame. */ void setSuperimposeVideoFrame(const RawFrame* videoSource); /** Set the VDP frame on which to superimpose the 'normal' output of * this PostProcessor. This is similar to the method above, except * that now the superimposing is done before scaling. IOW both frames * get scaled. */ void setSuperimposeVdpFrame(const FrameSource* vdpSource); /** Start/stop recording. * @param recorder Finished frames should be pushed to this * AviRecorder. Can also be nullptr, meaning * recording is stopped. */ void setRecorder(AviRecorder* recorder); /** Is recording active. * ATM used to keep frameskip constant during recording. */ bool isRecording() const; /** Get the number of bits per pixel for the pixels in these frames. * @return Possible values are 15, 16 or 32 */ unsigned getBpp() const; /** Get the frame that would be displayed. E.g. so that it can be * superimposed over the output of another PostProcessor, see * setSuperimposeVdpFrame(). */ FrameSource* getPaintFrame() const { return paintFrame; } // VideoLayer virtual void takeRawScreenShot(unsigned height, const std::string& filename); CliComm& getCliComm(); protected: /** Returns the maximum width for lines [y..y+step). */ static unsigned getLineWidth(FrameSource* frame, unsigned y, unsigned step); PostProcessor( MSXMotherBoard& motherBoard, Display& display, OutputSurface& screen, const std::string& videoSource, unsigned maxWidth, unsigned height, bool canDoInterlace); /** Render settings */ RenderSettings& renderSettings; /** The surface which is visible to the user. */ OutputSurface& screen; /** The last finished frame, ready to be displayed. */ std::unique_ptr currFrame; /** The frame before currFrame, ready to be displayed. */ std::unique_ptr prevFrame; /** Combined currFrame and prevFrame. */ std::unique_ptr deinterlacedFrame; /** Each line of currFrame twice, to get double vertical resolution. */ std::unique_ptr interlacedFrame; /** Result of superimposing 2 frames. */ std::unique_ptr superImposedFrame; /** Represents a frame as it should be displayed. * This can be simply a RawFrame or two RawFrames combined in a * DeinterlacedFrame or DoubledFrame. */ FrameSource* paintFrame; /** Video recorder, nullptr when not recording. */ AviRecorder* recorder; /** Video frame on which to superimpose the (VDP) output. * nullptr when not superimposing. */ const RawFrame* superImposeVideoFrame; const FrameSource* superImposeVdpFrame; int interleaveCount; // for interleave-black-frame private: void getScaledFrame(unsigned height, const void** lines, std::vector& workBuffer); // Schedulable virtual void executeUntil(EmuTime::param time, int userData); Display& display; /** Laserdisc cannot do interlace (better: the current implementation * is not interlaced). In that case some internal stuff can be done * with less buffers. */ const bool canDoInterlace; EmuTime lastRotate; EventDistributor& eventDistributor; }; } // namespace openmsx #endif // POSTPROCESSOR_HH openmsx-0.10.0/src/video/SuperImposedVideoFrame.cc0000644000175000017500000000441012262345041022630 0ustar manuelmanuel00000000000000#include "SuperImposedVideoFrame.hh" #include "LineScalers.hh" #include "MemoryOps.hh" #include "vla.hh" #include "build-info.hh" #include namespace openmsx { template SuperImposedVideoFrame::SuperImposedVideoFrame( const FrameSource& src_, const FrameSource& super_, const PixelOperations& pixelOps_) : FrameSource(pixelOps_.getSDLPixelFormat()) , src(src_), super(super_), pixelOps(pixelOps_) { setHeight(src.getHeight()); } template unsigned SuperImposedVideoFrame::getLineWidth(unsigned line) const { unsigned width = src.getLineWidth(line); return (width == 1) ? 320 : width; } template const void* SuperImposedVideoFrame::getLineInfo( unsigned line, unsigned& width, void* buf1_, unsigned bufWidth) const { auto* buf1 = static_cast(buf1_); // Return minimum line width of 320. // We could check whether both inputs have width=1 and in that case // also return a line of width=1. But for now (laserdisc) this will // never happen. auto* srcLine = static_cast( src.getLineInfo(line, width, buf1, bufWidth)); if (width == 1) { width = 320; MemoryOps::MemSet memset; memset(buf1, 320, srcLine[0]); srcLine = buf1; } // (possibly) srcLine == buf1 // Adjust the two inputs to the same height. const Pixel* supLine; VLA_SSE_ALIGNED(Pixel, buf2, width); assert(super.getHeight() == 480); // TODO possibly extend in the future if (src.getHeight() == 240) { VLA_SSE_ALIGNED(Pixel, buf3, width); auto* sup0 = super.getLinePtr(2 * line + 0, width, buf2); auto* sup1 = super.getLinePtr(2 * line + 1, width, buf3); BlendLines blend(pixelOps); blend(sup0, sup1, buf2, width); // possibly sup0 == buf2 supLine = buf2; } else { assert(src.getHeight() == super.getHeight()); supLine = super.getLinePtr(line, width, buf2); // scale line } // (possibly) supLine == buf2 // Actually blend the lines of both frames. AlphaBlendLines blend(pixelOps); blend(srcLine, supLine, buf1, width); // possibly srcLine == buf1 return buf1; } // Force template instantiation. #if HAVE_16BPP template class SuperImposedVideoFrame; #endif #if HAVE_32BPP template class SuperImposedVideoFrame; #endif } // namespace openmsx openmsx-0.10.0/src/video/VideoSystemChangeListener.hh0000644000175000017500000000046712262345041023360 0ustar manuelmanuel00000000000000#ifndef VIDEOSYSTEMCHANGELISTENER_HH #define VIDEOSYSTEMCHANGELISTENER_HH namespace openmsx { class VideoSystemChangeListener { public: virtual void preVideoSystemChange() = 0; virtual void postVideoSystemChange() = 0; protected: virtual ~VideoSystemChangeListener() {} }; } // namespace openmsx #endif openmsx-0.10.0/src/video/SDLRasterizer.cc0000644000175000017500000004413512262345041020754 0ustar manuelmanuel00000000000000#include "SDLRasterizer.hh" #include "VDP.hh" #include "VDPVRAM.hh" #include "RawFrame.hh" #include "CharacterConverter.hh" #include "BitmapConverter.hh" #include "SpriteConverter.hh" #include "MSXMotherBoard.hh" #include "Display.hh" #include "Renderer.hh" #include "RenderSettings.hh" #include "PostProcessor.hh" #include "FloatSetting.hh" #include "StringSetting.hh" #include "MemoryOps.hh" #include "VisibleSurface.hh" #include "memory.hh" #include "build-info.hh" #include "components.hh" #include #include #include namespace openmsx { /** VDP ticks between start of line and start of left border. */ static const int TICKS_LEFT_BORDER = 100 + 102; /** The middle of the visible (display + borders) part of a line, * expressed in VDP ticks since the start of the line. * TODO: Move this to a central location? */ static const int TICKS_VISIBLE_MIDDLE = TICKS_LEFT_BORDER + (VDP::TICKS_PER_LINE - TICKS_LEFT_BORDER - 27) / 2; template inline int SDLRasterizer::translateX(int absoluteX, bool narrow) { int maxX = narrow ? 640 : 320; if (absoluteX == VDP::TICKS_PER_LINE) return maxX; // Note: The ROUND_MASK forces the ticks to a pixel (2-tick) boundary. // If this is not done, rounding errors will occur. // This is especially tricky because division of a negative number // is rounded towards zero instead of down. const int ROUND_MASK = narrow ? ~1 : ~3; int screenX = ((absoluteX & ROUND_MASK) - (TICKS_VISIBLE_MIDDLE & ROUND_MASK)) / (narrow ? 2 : 4) + maxX / 2; return std::max(screenX, 0); } template inline void SDLRasterizer::renderBitmapLine(Pixel* buf, unsigned vramLine) { if (vdp.getDisplayMode().isPlanar()) { const byte* vramPtr0; const byte* vramPtr1; vram.bitmapCacheWindow.getReadAreaPlanar( vramLine * 256, 256, vramPtr0, vramPtr1); bitmapConverter->convertLinePlanar(buf, vramPtr0, vramPtr1); } else { const byte* vramPtr = vram.bitmapCacheWindow.getReadArea(vramLine * 128, 128); bitmapConverter->convertLine(buf, vramPtr); } } template SDLRasterizer::SDLRasterizer( VDP& vdp_, Display& display, VisibleSurface& screen_, std::unique_ptr postProcessor_) : vdp(vdp_), vram(vdp.getVRAM()) , screen(screen_) , postProcessor(std::move(postProcessor_)) , workFrame(make_unique(screen.getSDLFormat(), 640, 240)) , renderSettings(display.getRenderSettings()) , characterConverter(make_unique>( vdp, palFg, palBg)) , bitmapConverter(make_unique>( palFg, PALETTE256, V9958_COLORS)) , spriteConverter(make_unique>( vdp.getSpriteChecker())) { // Init the palette. precalcPalette(); // Initialize palette (avoid UMR) if (!vdp.isMSX1VDP()) { for (int i = 0; i < 16; ++i) { palFg[i] = palFg[i + 16] = palBg[i] = V9938_COLORS[0][0][0]; } } renderSettings.getGamma() .attach(*this); renderSettings.getBrightness() .attach(*this); renderSettings.getContrast() .attach(*this); renderSettings.getColorMatrix().attach(*this); } template SDLRasterizer::~SDLRasterizer() { renderSettings.getColorMatrix().detach(*this); renderSettings.getGamma() .detach(*this); renderSettings.getBrightness() .detach(*this); renderSettings.getContrast() .detach(*this); } template PostProcessor* SDLRasterizer::getPostProcessor() const { return postProcessor.get(); } template bool SDLRasterizer::isActive() { return postProcessor->needRender() && vdp.getMotherBoard().isActive() && !vdp.getMotherBoard().isFastForwarding(); } template void SDLRasterizer::reset() { // Init renderer state. setDisplayMode(vdp.getDisplayMode()); spriteConverter->setTransparency(vdp.getTransparency()); resetPalette(); } template void SDLRasterizer::resetPalette() { if (!vdp.isMSX1VDP()) { // Reset the palette. for (int i = 0; i < 16; i++) { setPalette(i, vdp.getPalette(i)); } } } template void SDLRasterizer::setSuperimposeVideoFrame(const RawFrame* videoSource) { postProcessor->setSuperimposeVideoFrame(videoSource); precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(), videoSource, vdp.getBackgroundColor()); } template void SDLRasterizer::frameStart(EmuTime::param time) { workFrame = postProcessor->rotateFrames(std::move(workFrame), vdp.isInterlaced() ? (vdp.getEvenOdd() ? FrameSource::FIELD_ODD : FrameSource::FIELD_EVEN) : FrameSource::FIELD_NONINTERLACED, time); // Calculate line to render at top of screen. // Make sure the display area is centered. // 240 - 212 = 28 lines available for top/bottom border; 14 each. // NTSC: display at [32..244), // PAL: display at [59..271). lineRenderTop = vdp.isPalTiming() ? 59 - 14 : 32 - 14; } template void SDLRasterizer::frameEnd() { // Nothing to do. } template void SDLRasterizer::setDisplayMode(DisplayMode mode) { if (mode.isBitmapMode()) { bitmapConverter->setDisplayMode(mode); } else { characterConverter->setDisplayMode(mode); } precalcColorIndex0(mode, vdp.getTransparency(), vdp.isSuperimposing(), vdp.getBackgroundColor()); spriteConverter->setDisplayMode(mode); spriteConverter->setPalette(mode.getByte() == DisplayMode::GRAPHIC7 ? palGraphic7Sprites : palBg); } template void SDLRasterizer::setPalette(int index, int grb) { // Update SDL colors in palette. Pixel newColor = V9938_COLORS[(grb >> 4) & 7][grb >> 8][grb & 7]; palFg[index ] = newColor; palFg[index + 16] = newColor; palBg[index ] = newColor; bitmapConverter->palette16Changed(); precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(), vdp.isSuperimposing(), vdp.getBackgroundColor()); } template void SDLRasterizer::setBackgroundColor(int index) { precalcColorIndex0(vdp.getDisplayMode(), vdp.getTransparency(), vdp.isSuperimposing(), index); } template void SDLRasterizer::setTransparency(bool enabled) { spriteConverter->setTransparency(enabled); precalcColorIndex0(vdp.getDisplayMode(), enabled, vdp.isSuperimposing(), vdp.getBackgroundColor()); } template void SDLRasterizer::precalcPalette() { if (vdp.isMSX1VDP()) { // Fixed palette. for (int i = 0; i < 16; i++) { const byte* rgb = Renderer::TMS99X8A_PALETTE[i]; double dr = rgb[0] / 255.0; double dg = rgb[1] / 255.0; double db = rgb[2] / 255.0; renderSettings.transformRGB(dr, dg, db); palFg[i] = palFg[i + 16] = palBg[i] = screen.mapKeyedRGB(dr, dg, db); } } else { if (vdp.hasYJK()) { // Precalculate palette for V9958 colors. if (renderSettings.isColorMatrixIdentity()) { // Most users use the "normal" monitor type; making this a // special case speeds up palette precalculation a lot. int intensity[32]; for (int i = 0; i < 32; i++) { intensity[i] = int(255 * renderSettings.transformComponent(i / 31.0)); } for (int rgb = 0; rgb < (1 << 15); rgb++) { V9958_COLORS[rgb] = screen.mapKeyedRGB( intensity[(rgb >> 10) ], intensity[(rgb >> 5) & 31], intensity[ rgb & 31]); } } else { for (int r5 = 0; r5 < 32; r5++) { for (int g5 = 0; g5 < 32; g5++) { for (int b5 = 0; b5 < 32; b5++) { double dr = r5 / 31.0; double dg = g5 / 31.0; double db = b5 / 31.0; renderSettings.transformRGB(dr, dg, db); V9958_COLORS[(r5<<10) + (g5<<5) + b5] = screen.mapKeyedRGB(dr, dg, db); } } } } // Precalculate palette for V9938 colors. // Based on comparing red and green gradients, using palette and // YJK, in SCREEN11 on a real turbo R. for (int r3 = 0; r3 < 8; r3++) { int r5 = (r3 << 2) | (r3 >> 1); for (int g3 = 0; g3 < 8; g3++) { int g5 = (g3 << 2) | (g3 >> 1); for (int b3 = 0; b3 < 8; b3++) { int b5 = (b3 << 2) | (b3 >> 1); V9938_COLORS[r3][g3][b3] = V9958_COLORS[(r5<<10) + (g5<<5) + b5]; } } } } else { // Precalculate palette for V9938 colors. if (renderSettings.isColorMatrixIdentity()) { int intensity[8]; for (int i = 0; i < 8; i++) { intensity[i] = int(255 * renderSettings.transformComponent(i / 7.0)); } for (int r3 = 0; r3 < 8; r3++) { for (int g3 = 0; g3 < 8; g3++) { for (int b3 = 0; b3 < 8; b3++) { V9938_COLORS[r3][g3][b3] = screen.mapKeyedRGB( intensity[r3], intensity[g3], intensity[b3] ); } } } } else { for (int r3 = 0; r3 < 8; r3++) { for (int g3 = 0; g3 < 8; g3++) { for (int b3 = 0; b3 < 8; b3++) { double dr = r3 / 7.0; double dg = g3 / 7.0; double db = b3 / 7.0; renderSettings.transformRGB(dr, dg, db); V9938_COLORS[r3][g3][b3] = screen.mapKeyedRGB(dr, dg, db); } } } } } // Precalculate Graphic 7 bitmap palette. for (int i = 0; i < 256; i++) { PALETTE256[i] = V9938_COLORS [(i & 0x1C) >> 2] [(i & 0xE0) >> 5] [(i & 0x03) == 3 ? 7 : (i & 0x03) * 2]; } // Precalculate Graphic 7 sprite palette. for (int i = 0; i < 16; i++) { uint16_t grb = Renderer::GRAPHIC7_SPRITE_PALETTE[i]; palGraphic7Sprites[i] = V9938_COLORS[(grb >> 4) & 7][grb >> 8][grb & 7]; } } } template void SDLRasterizer::precalcColorIndex0(DisplayMode mode, bool transparency, const RawFrame* superimposing, byte bgcolorIndex) { // Graphic7 mode doesn't use transparency. if (mode.getByte() == DisplayMode::GRAPHIC7) { transparency = false; } int tpIndex = transparency ? bgcolorIndex : 0; if (mode.getBase() != DisplayMode::GRAPHIC5) { Pixel c = (superimposing && (bgcolorIndex == 0)) ? screen.getKeyColor() : palBg[tpIndex]; if (palFg[0] != c) { palFg[0] = c; bitmapConverter->palette16Changed(); } } else { // TODO: superimposing if ((palFg[ 0] != palBg[tpIndex >> 2]) || (palFg[16] != palBg[tpIndex & 3])) { palFg[ 0] = palBg[tpIndex >> 2]; palFg[16] = palBg[tpIndex & 3]; bitmapConverter->palette16Changed(); } } } template void SDLRasterizer::drawBorder( int fromX, int fromY, int limitX, int limitY) { DisplayMode mode = vdp.getDisplayMode(); byte modeBase = mode.getBase(); int bgColor = vdp.getBackgroundColor(); Pixel border0, border1; if (modeBase == DisplayMode::GRAPHIC5) { // border in SCREEN6 has separate color for even and odd pixels. // TODO odd/even swapped? border0 = palBg[(bgColor & 0x0C) >> 2]; border1 = palBg[(bgColor & 0x03) >> 0]; } else if (modeBase == DisplayMode::GRAPHIC7) { border0 = border1 = PALETTE256[bgColor]; } else { if (!bgColor && vdp.isSuperimposing()) { border0 = border1 = screen.getKeyColor(); } else { border0 = border1 = palBg[bgColor]; } } int startY = std::max(fromY - lineRenderTop, 0); int endY = std::min(limitY - lineRenderTop, 240); if ((fromX == 0) && (limitX == VDP::TICKS_PER_LINE) && (border0 == border1)) { // complete lines, non striped for (int y = startY; y < endY; y++) { workFrame->setBlank(y, border0); } } else { unsigned lineWidth = mode.getLineWidth(); unsigned x = translateX(fromX, (lineWidth == 512)); unsigned num = translateX(limitX, (lineWidth == 512)) - x; unsigned width = (lineWidth == 512) ? 640 : 320; MemoryOps::MemSet2 memset; for (int y = startY; y < endY; ++y) { memset(workFrame->getLinePtrDirect(y) + x, num, border0, border1); workFrame->setLineWidth(y, width); } } } template void SDLRasterizer::drawDisplay( int /*fromX*/, int fromY, int displayX, int displayY, int displayWidth, int displayHeight) { DisplayMode mode = vdp.getDisplayMode(); unsigned lineWidth = mode.getLineWidth(); if (lineWidth == 256) { int endX = displayX + displayWidth; displayX /= 2; displayWidth = endX / 2 - displayX; } // Clip to screen area. int screenLimitY = std::min( fromY + displayHeight - lineRenderTop, 240); int screenY = fromY - lineRenderTop; if (screenY < 0) { displayY -= screenY; fromY = lineRenderTop; screenY = 0; } displayHeight = screenLimitY - screenY; if (displayHeight <= 0) return; int leftBackground = translateX(vdp.getLeftBackground(), lineWidth == 512); // TODO: Find out why this causes 1-pixel jitter: //dest.x = translateX(fromX); int hScroll = mode.isTextMode() ? 0 : 8 * (lineWidth / 256) * (vdp.getHorizontalScrollHigh() & 0x1F); // Page border is display X coordinate where to stop drawing current page. // This is either the multi page split point, or the right edge of the // rectangle to draw, whichever comes first. // Note that it is possible for pageBorder to be to the left of displayX, // in that case only the second page should be drawn. int pageBorder = displayX + displayWidth; int scrollPage1, scrollPage2; if (vdp.isMultiPageScrolling()) { scrollPage1 = vdp.getHorizontalScrollHigh() >> 5; scrollPage2 = scrollPage1 ^ 1; } else { scrollPage1 = 0; scrollPage2 = 0; } // Because SDL blits do not wrap, unlike GL textures, the pageBorder is // also used if multi page is disabled. int pageSplit = lineWidth - hScroll; if (pageSplit < pageBorder) { pageBorder = pageSplit; } if (mode.isBitmapMode()) { // Which bits in the name mask determine the page? int pageMaskOdd = (mode.isPlanar() ? 0x000 : 0x200) | vdp.getEvenOddMask(); int pageMaskEven = vdp.isMultiPageScrolling() ? (pageMaskOdd & ~0x100) : pageMaskOdd; for (int y = screenY; y < screenLimitY; y++) { const int vramLine[2] = { (vram.nameTable.getMask() >> 7) & (pageMaskEven | displayY), (vram.nameTable.getMask() >> 7) & (pageMaskOdd | displayY) }; Pixel buf[512]; int lineInBuf = -1; // buffer data not valid Pixel* dst = workFrame->getLinePtrDirect(y) + leftBackground + displayX; int firstPageWidth = pageBorder - displayX; if (firstPageWidth > 0) { if ((displayX + hScroll) == 0) { renderBitmapLine(dst, vramLine[scrollPage1]); } else { lineInBuf = vramLine[scrollPage1]; renderBitmapLine(buf, vramLine[scrollPage1]); const Pixel* src = buf + displayX + hScroll; memcpy(dst, src, firstPageWidth * sizeof(Pixel)); } } else { firstPageWidth = 0; } if (firstPageWidth < displayWidth) { if (lineInBuf != vramLine[scrollPage2]) { renderBitmapLine(buf, vramLine[scrollPage2]); } unsigned x = displayX < pageBorder ? 0 : displayX + hScroll - lineWidth; memcpy(dst + firstPageWidth, buf + x, (displayWidth - firstPageWidth) * sizeof(Pixel)); } workFrame->setLineWidth(y, (lineWidth == 512) ? 640 : 320); displayY = (displayY + 1) & 255; } } else { // horizontal scroll (high) is implemented in CharacterConverter for (int y = screenY; y < screenLimitY; y++) { assert(!vdp.isMSX1VDP() || displayY < 192); Pixel* dst = workFrame->getLinePtrDirect(y) + leftBackground + displayX; if (displayX == 0) { characterConverter->convertLine(dst, displayY); } else { Pixel buf[512]; characterConverter->convertLine(buf, displayY); const Pixel* src = buf + displayX; memcpy(dst, src, displayWidth * sizeof(Pixel)); } workFrame->setLineWidth(y, (lineWidth == 512) ? 640 : 320); displayY = (displayY + 1) & 255; } } } template void SDLRasterizer::drawSprites( int /*fromX*/, int fromY, int displayX, int displayY, int displayWidth, int displayHeight) { // Clip to screen area. // TODO: Code duplicated from drawDisplay. int screenLimitY = std::min( fromY + displayHeight - lineRenderTop, 240); int screenY = fromY - lineRenderTop; if (screenY < 0) { displayY -= screenY; fromY = lineRenderTop; screenY = 0; } displayHeight = screenLimitY - screenY; if (displayHeight <= 0) return; // Render sprites. // TODO: Call different SpriteConverter methods depending on narrow/wide // pixels in this display mode? int spriteMode = vdp.getDisplayMode().getSpriteMode(); int displayLimitX = displayX + displayWidth; int limitY = fromY + displayHeight; int screenX = translateX( vdp.getLeftSprites(), vdp.getDisplayMode().getLineWidth() == 512); if (spriteMode == 1) { for (int y = fromY; y < limitY; y++, screenY++) { Pixel* pixelPtr = workFrame->getLinePtrDirect(screenY) + screenX; spriteConverter->drawMode1(y, displayX, displayLimitX, pixelPtr); } } else { byte mode = vdp.getDisplayMode().getByte(); if (mode == DisplayMode::GRAPHIC5) { for (int y = fromY; y < limitY; y++, screenY++) { Pixel* pixelPtr = workFrame->getLinePtrDirect(screenY) + screenX; spriteConverter->template drawMode2( y, displayX, displayLimitX, pixelPtr); } } else if (mode == DisplayMode::GRAPHIC6) { for (int y = fromY; y < limitY; y++, screenY++) { Pixel* pixelPtr = workFrame->getLinePtrDirect(screenY) + screenX; spriteConverter->template drawMode2( y, displayX, displayLimitX, pixelPtr); } } else { for (int y = fromY; y < limitY; y++, screenY++) { Pixel* pixelPtr = workFrame->getLinePtrDirect(screenY) + screenX; spriteConverter->template drawMode2( y, displayX, displayLimitX, pixelPtr); } } } } template bool SDLRasterizer::isRecording() const { return postProcessor->isRecording(); } template void SDLRasterizer::update(const Setting& setting) { if ((&setting == &renderSettings.getGamma()) || (&setting == &renderSettings.getBrightness()) || (&setting == &renderSettings.getContrast()) || (&setting == &renderSettings.getColorMatrix())) { precalcPalette(); resetPalette(); } } // Force template instantiation. #if HAVE_16BPP template class SDLRasterizer; #endif #if HAVE_32BPP || COMPONENT_GL template class SDLRasterizer; #endif } // namespace openmsx openmsx-0.10.0/src/video/ld/0000755000175000017500000000000012262345041016340 5ustar manuelmanuel00000000000000openmsx-0.10.0/src/video/ld/node.mk0000644000175000017500000000026312262345041017617 0ustar manuelmanuel00000000000000include build/node-start.mk SRC_HDR_$(COMPONENT_LASERDISC)+= \ LDSDLRasterizer \ LDPixelRenderer LDRenderer LDDummyRenderer HDR_ONLY:= LDRasterizer include build/node-end.mk openmsx-0.10.0/src/video/ld/LDDummyRenderer.hh0000644000175000017500000000052612262345041021666 0ustar manuelmanuel00000000000000#ifndef LDDUMMYRENDERER_HH #define LDDUMMYRENDERER_HH #include "LDRenderer.hh" namespace openmsx { class LDDummyRenderer : public LDRenderer { public: virtual void frameStart(EmuTime::param time); virtual void frameEnd(); virtual void drawBlank(int r, int g, int b); virtual RawFrame* getRawFrame(); }; } // namespace openmsx #endif openmsx-0.10.0/src/video/ld/LDSDLRasterizer.cc0000644000175000017500000000310712262345041021565 0ustar manuelmanuel00000000000000#include "LDSDLRasterizer.hh" #include "RawFrame.hh" #include "PostProcessor.hh" #include "VisibleSurface.hh" #include "memory.hh" #include "build-info.hh" #include "components.hh" #include namespace openmsx { template LDSDLRasterizer::LDSDLRasterizer( VisibleSurface& screen, std::unique_ptr postProcessor_) : postProcessor(std::move(postProcessor_)) , workFrame(make_unique(screen.getSDLFormat(), 640, 480)) , pixelFormat(screen.getSDLFormat()) { } template LDSDLRasterizer::~LDSDLRasterizer() { } template PostProcessor* LDSDLRasterizer::getPostProcessor() const { return postProcessor.get(); } template void LDSDLRasterizer::frameStart(EmuTime::param time) { workFrame = postProcessor->rotateFrames(std::move(workFrame), FrameSource::FIELD_NONINTERLACED, time); } template void LDSDLRasterizer::drawBlank(int r, int g, int b) { // We should really be presenting the "LASERVISION" text // here, like the real laserdisc player does. Note that this // changes when seeking or starting to play. auto background = static_cast(SDL_MapRGB(&pixelFormat, r, g, b)); for (int y = 0; y < 480; ++y) { workFrame->setBlank(y, background); } } template RawFrame* LDSDLRasterizer::getRawFrame() { return workFrame.get(); } // Force template instantiation. #if HAVE_16BPP template class LDSDLRasterizer; #endif #if HAVE_32BPP || COMPONENT_GL template class LDSDLRasterizer; #endif } // namespace openmsx openmsx-0.10.0/src/video/ld/LDRenderer.hh0000644000175000017500000000172712262345041020656 0ustar manuelmanuel00000000000000#ifndef LDRENDERER_HH #define LDRENDERER_HH #include "EmuTime.hh" namespace openmsx { class RawFrame; /** Abstract base class for LDRenderers. * A LDRenderer is a class that converts VDP state to visual * information (for example, pixels on a screen). * * The update methods are called exactly before the change occurs in * the VDP, so that the renderer can update itself to the specified * time using the old settings. */ class LDRenderer { public: virtual ~LDRenderer(); /** Signals the start of a new frame. * The LDRenderer can use this to get fixed-per-frame settings from * the VDP, such as PAL/NTSC timing. * @param time The moment in emulated time the frame starts. */ virtual void frameStart(EmuTime::param time) = 0; /** Signals the end of a frame. */ virtual void frameEnd() = 0; virtual void drawBlank(int r, int g, int b) = 0; virtual RawFrame* getRawFrame() = 0; protected: LDRenderer(); }; } // namespace openmsx #endif openmsx-0.10.0/src/video/ld/LDPixelRenderer.cc0000644000175000017500000000225712262345041021645 0ustar manuelmanuel00000000000000#include "LDPixelRenderer.hh" #include "LDRasterizer.hh" #include "PostProcessor.hh" #include "Display.hh" #include "VideoSystem.hh" #include "VideoSourceSetting.hh" #include "EventDistributor.hh" #include "FinishFrameEvent.hh" #include "MSXMotherBoard.hh" #include "LaserdiscPlayer.hh" #include "Reactor.hh" namespace openmsx { LDPixelRenderer::LDPixelRenderer(LaserdiscPlayer& ld, Display& display) : motherboard(ld.getMotherBoard()) , eventDistributor(motherboard.getReactor().getEventDistributor()) , rasterizer(display.getVideoSystem().createLDRasterizer(ld)) { } LDPixelRenderer::~LDPixelRenderer() { } void LDPixelRenderer::frameStart(EmuTime::param time) { rasterizer->frameStart(time); } bool LDPixelRenderer::isActive() const { return motherboard.isActive(); } void LDPixelRenderer::frameEnd() { eventDistributor.distributeEvent(std::make_shared( rasterizer->getPostProcessor()->getVideoSource(), motherboard.getVideoSource().getSource(), !isActive())); } void LDPixelRenderer::drawBlank(int r, int g, int b ) { rasterizer->drawBlank(r, g, b); } RawFrame* LDPixelRenderer::getRawFrame() { return rasterizer->getRawFrame(); } } // namespace openmsx openmsx-0.10.0/src/video/ld/LDDummyRenderer.cc0000644000175000017500000000046512262345041021656 0ustar manuelmanuel00000000000000#include "LDDummyRenderer.hh" namespace openmsx { void LDDummyRenderer::frameStart(EmuTime::param /*time*/) { } void LDDummyRenderer::frameEnd() { } void LDDummyRenderer::drawBlank(int /*r*/, int /*g*/, int /*b*/) { } RawFrame* LDDummyRenderer::getRawFrame() { return nullptr; } } // namespace openmsx openmsx-0.10.0/src/video/ld/LDRenderer.cc0000644000175000017500000000020312262345041020630 0ustar manuelmanuel00000000000000#include "LDRenderer.hh" namespace openmsx { LDRenderer::LDRenderer() { } LDRenderer::~LDRenderer() { } } // namespace openmsx openmsx-0.10.0/src/video/ld/LDRasterizer.hh0000644000175000017500000000064012262345041021233 0ustar manuelmanuel00000000000000#ifndef LDRASTERIZER_HH #define LDRASTERIZER_HH #include "EmuTime.hh" namespace openmsx { class PostProcessor; class RawFrame; class LDRasterizer { public: virtual ~LDRasterizer() {} virtual PostProcessor* getPostProcessor() const = 0; virtual void frameStart(EmuTime::param time) = 0; virtual void drawBlank(int r, int g, int b) = 0; virtual RawFrame* getRawFrame() = 0; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/ld/LDPixelRenderer.hh0000644000175000017500000000161212262345041021651 0ustar manuelmanuel00000000000000#ifndef LDPIXELRENDERER_HH #define LDPIXELRENDERER_HH #include "LDRenderer.hh" #include "noncopyable.hh" #include namespace openmsx { class MSXMotherBoard; class EventDistributor; class Display; class LDRasterizer; class LaserdiscPlayer; /** Generic implementation of a pixel-based Renderer. * Uses a Rasterizer to plot actual pixels for a specific video system. */ class LDPixelRenderer : public LDRenderer, private noncopyable { public: LDPixelRenderer(LaserdiscPlayer& ld, Display& display); virtual ~LDPixelRenderer(); // Renderer interface: virtual void frameStart(EmuTime::param time); virtual void frameEnd(); virtual void drawBlank(int r, int g, int b); virtual RawFrame* getRawFrame(); private: bool isActive() const; MSXMotherBoard& motherboard; EventDistributor& eventDistributor; const std::unique_ptr rasterizer; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/ld/LDSDLRasterizer.hh0000644000175000017500000000212112262345041021572 0ustar manuelmanuel00000000000000#ifndef LDSDLRASTERIZER_HH #define LDSDLRASTERIZER_HH #include "LDRasterizer.hh" #include "noncopyable.hh" #include #include namespace openmsx { class VisibleSurface; class RawFrame; class PostProcessor; /** Rasterizer using a frame buffer approach: it writes pixels to a single * rectangular pixel buffer. */ template class LDSDLRasterizer : public LDRasterizer, private noncopyable { public: LDSDLRasterizer( VisibleSurface& screen, std::unique_ptr postProcessor); virtual ~LDSDLRasterizer(); // Rasterizer interface: virtual PostProcessor* getPostProcessor() const; virtual void frameStart(EmuTime::param time); virtual void drawBlank(int r, int g, int b); virtual RawFrame* getRawFrame(); private: /** The video post processor which displays the frames produced by this * rasterizer. */ const std::unique_ptr postProcessor; /** The next frame as it is delivered by the VDP, work in progress. */ std::unique_ptr workFrame; const SDL_PixelFormat pixelFormat; }; } // namespace openmsx #endif openmsx-0.10.0/src/video/node.mk0000644000175000017500000000170412262345041017221 0ustar manuelmanuel00000000000000include build/node-start.mk SUBDIRS:= \ v9990 ld scalers SRC_HDR:= \ VDP VDPAccessSlots VDPCmdEngine VDPVRAM SpriteChecker ADVram \ Renderer RendererFactory RenderSettings PixelRenderer \ SDLVideoSystem SDLRasterizer FBPostProcessor SDLSnow \ DummyRenderer \ BitmapConverter CharacterConverter \ PNG \ VideoSystem Display VideoLayer \ Layer \ DummyVideoSystem \ SDLImage BaseImage \ FrameSource RawFrame PostProcessor \ DeinterlacedFrame DoubledFrame \ SuperImposedFrame SuperImposedVideoFrame \ OutputSurface VisibleSurface SDLVisibleSurface \ SDLOffScreenSurface \ Icon \ AviRecorder AviWriter ZMBVEncoder HDR_ONLY:= \ DisplayMode \ VRAMObserver \ SpriteConverter \ PixelOperations \ Rasterizer \ VideoSystemChangeListener \ LayerListener \ SDLSurfacePtr \ OutputRectangle SRC_HDR_$(COMPONENT_GL)+= \ SDLGLOutputSurface SDLGLVisibleSurface SDLGLOffScreenSurface \ GLSnow GLUtil GLImage GLPostProcessor include build/node-end.mk openmsx-0.10.0/src/video/FBPostProcessor.hh0000644000175000017500000000301412262345041021315 0ustar manuelmanuel00000000000000#ifndef FBPOSTPROCESSOR_HH #define FBPOSTPROCESSOR_HH #include "PostProcessor.hh" #include "RenderSettings.hh" #include "PixelOperations.hh" #include namespace openmsx { class MSXMotherBoard; class Display; template class Scaler; /** Rasterizer using SDL. */ template class FBPostProcessor : public PostProcessor { public: FBPostProcessor( MSXMotherBoard& motherBoard, Display& display, OutputSurface& screen, const std::string& videoSource, unsigned maxWidth, unsigned height, bool canDoInterlace); virtual ~FBPostProcessor(); // Layer interface: virtual void paint(OutputSurface& output); virtual std::unique_ptr rotateFrames( std::unique_ptr finishedFrame, FrameSource::FieldType field, EmuTime::param time); private: void preCalcNoise(double factor); void drawNoise(OutputSurface& output); void drawNoiseLine(Pixel* buf, signed char* noise, unsigned long width); // Observer virtual void update(const Setting& setting); /** The currently active scaler. */ std::unique_ptr> currScaler; /** Currently active scale algorithm, used to detect scaler changes. */ RenderSettings::ScaleAlgorithm scaleAlgorithm; /** Currently active scale factor, used to detect scaler changes. */ unsigned scaleFactor; /** Remember the noise values to get a stable image when paused. */ std::vector noiseShift; PixelOperations pixelOps; }; } // namespace openmsx #endif // FBPOSTPROCESSOR_HH openmsx-0.10.0/src/video/VDPAccessSlots.cc0000644000175000017500000001040512262345041021050 0ustar manuelmanuel00000000000000#include "VDPAccessSlots.hh" namespace openmsx { namespace VDPAccessSlots { // TODO the following 3 tables are correct for bitmap screen modes, // still need to investigate character and text modes. // These tables must contain at least one value that is bigger or equal // to 1368+136. So we extend the data with some cyclic duplicates. static const int16_t slotsScreenOff[154 + 17] = { 0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 164, 172, 180, 188, 196, 204, 212, 220, 228, 236, 244, 252, 260, 268, 276, 292, 300, 308, 316, 324, 332, 340, 348, 356, 364, 372, 380, 388, 396, 404, 420, 428, 436, 444, 452, 460, 468, 476, 484, 492, 500, 508, 516, 524, 532, 548, 556, 564, 572, 580, 588, 596, 604, 612, 620, 628, 636, 644, 652, 660, 676, 684, 692, 700, 708, 716, 724, 732, 740, 748, 756, 764, 772, 780, 788, 804, 812, 820, 828, 836, 844, 852, 860, 868, 876, 884, 892, 900, 908, 916, 932, 940, 948, 956, 964, 972, 980, 988, 996, 1004, 1012, 1020, 1028, 1036, 1044, 1060, 1068, 1076, 1084, 1092, 1100, 1108, 1116, 1124, 1132, 1140, 1148, 1156, 1164, 1172, 1188, 1196, 1204, 1212, 1220, 1228, 1268, 1276, 1284, 1292, 1300, 1308, 1316, 1324, 1334, 1344, 1352, 1360, 1368+ 0, 1368+ 8, 1368+16, 1368+ 24, 1368+ 32, 1368+ 40, 1368+ 48, 1368+56, 1368+ 64, 1368+ 72, 1368+ 80, 1368+ 88, 1368+96, 1368+104, 1368+112, 1368+120, 1368+164 }; static const int16_t slotsSpritesOff[88 + 16] = { 6, 14, 22, 30, 38, 46, 54, 62, 70, 78, 86, 94, 102, 110, 118, 162, 170, 182, 188, 214, 220, 246, 252, 278, 310, 316, 342, 348, 374, 380, 406, 438, 444, 470, 476, 502, 508, 534, 566, 572, 598, 604, 630, 636, 662, 694, 700, 726, 732, 758, 764, 790, 822, 828, 854, 860, 886, 892, 918, 950, 956, 982, 988, 1014, 1020, 1046, 1078, 1084, 1110, 1116, 1142, 1148, 1174, 1206, 1212, 1266, 1274, 1282, 1290, 1298, 1306, 1314, 1322, 1332, 1342, 1350, 1358, 1366, 1368+ 6, 1368+14, 1368+ 22, 1368+ 30, 1368+ 38, 1368+ 46, 1368+54, 1368+ 62, 1368+ 70, 1368+ 78, 1368+ 86, 1368+94, 1368+102, 1368+110, 1368+118, 1368+162, }; static const int16_t slotsSpritesOn[31 + 3] = { 28, 92, 162, 170, 188, 220, 252, 316, 348, 380, 444, 476, 508, 572, 604, 636, 700, 732, 764, 828, 860, 892, 956, 988, 1020, 1084, 1116, 1148, 1212, 1264, 1330, 1368+28, 1368+92, 1368+162, }; static int16_t tabSpritesOn [NUM_DELTAS * TICKS]; static int16_t tabSpritesOff[NUM_DELTAS * TICKS]; static int16_t tabScreenOff [NUM_DELTAS * TICKS]; static int16_t tabBroken [NUM_DELTAS * TICKS]; static void initTable(const int16_t* slots, int16_t* output) { // !!! Keep this in sync with the 'Delta' enum !!! static const int delta[NUM_DELTAS] = { 0, 1, 16, 24, 32, 40, 48, 64, 72, 88, 104, 120, 128, 136 }; for (int i = 0; i < NUM_DELTAS; ++i) { int step = delta[i]; int p = 0; while (slots[p] < step) ++p; for (int j = 0; j < TICKS; ++j) { if ((slots[p] - j) < step) ++p; assert((slots[p] - j) >= step); *output++ = slots[p]; } } } void initTables() { static bool init = false; if (init) return; init = true; initTable(slotsSpritesOn, tabSpritesOn); initTable(slotsSpritesOff, tabSpritesOff); initTable(slotsScreenOff, tabScreenOff); for (int i = 0; i < NUM_DELTAS; ++i) { for (int j = 0; j < TICKS; ++j) { tabBroken[i * TICKS + j] = j; } } } static const int16_t* getTab(bool display, bool sprites, bool broken) { return broken ? tabBroken : (display ? (sprites ? tabSpritesOn : tabSpritesOff) : tabScreenOff); } EmuTime getAccessSlot( EmuTime::param frame_, EmuTime::param time, Delta delta, bool display, bool sprites, bool broken) { VDP::VDPClock frame(frame_); unsigned ticks = frame.getTicksTill_fast(time) % TICKS; auto* tab = getTab(display, sprites, broken); return time + VDP::VDPClock::duration(tab[delta + ticks] - ticks); } Calculator getCalculator( EmuTime::param frame, EmuTime::param time, EmuTime::param limit, bool display, bool sprites, bool broken) { auto* tab = getTab(display, sprites, broken); return Calculator(frame, time, limit, tab); } } // namespace VDPAccessSlots } // namespace openmsx openmsx-0.10.0/src/video/VDPCmdEngine.hh0000644000175000017500000001777112262345041020502 0ustar manuelmanuel00000000000000#ifndef VDPCMDENGINE_HH #define VDPCMDENGINE_HH #include "VDP.hh" #include "VDPAccessSlots.hh" #include "DisplayMode.hh" #include "serialize_meta.hh" #include "openmsx.hh" #include "noncopyable.hh" #include "likely.hh" #include namespace openmsx { class VDPVRAM; class CommandController; class RenderSettings; class Setting; class BooleanSetting; class TclCallback; /** This is an abstract base class the VDP commands */ class VDPCmd { public: virtual ~VDPCmd() {} /** Prepare execution of cmd */ virtual void start(EmuTime::param time, VDPCmdEngine& engine) = 0; /** Perform a given V9938 graphical operation. */ virtual void execute(EmuTime::param limit, VDPCmdEngine& engine) = 0; }; /** VDP command engine by Alex Wulms. * Implements command execution unit of V9938/58. */ class VDPCmdEngine : private noncopyable { public: VDPCmdEngine(VDP& vdp, RenderSettings& renderSettings_, CommandController& commandController); virtual ~VDPCmdEngine(); /** Reinitialise Renderer state. * @param time The moment in time the reset occurs. */ void reset(EmuTime::param time); /** Synchronises the command engine with the VDP. * Ideally this would be a private method, but the current * design doesn't allow that. * @param time The moment in emulated time to sync to. */ inline void sync(EmuTime::param time) { if (!currentCommand) return; currentCommand->execute(time, *this); if (currentCommand && unlikely(vdp.cpuAccessScheduled())) { // If there's a CPU access scheduled, then the next // slot will be used by the CPU. So we take a later // slot. nextAccessSlot(VDPAccessSlots::DELTA_1); // skip one slot assert(this->time > time); } } /** Gets the command engine status (part of S#2). * Bit 7 (TR) is set when the command engine is ready for * a pixel transfer. * Bit 4 (BD) is set when the boundary color is detected. * Bit 0 (CE) is set when a command is in progress. */ inline byte getStatus(EmuTime::param time) { if (time >= statusChangeTime) { sync(time); } return status; } /** Use this method to transfer pixel(s) from VDP to CPU. * This method implements V9938 S#7. * @param time The moment in emulated time this read occurs. * @return Color value of the pixel. */ inline byte readColor(EmuTime::param time) { sync(time); return COL; } inline void resetColor() { // Note: Real VDP always resets TR, but for such a short time // that the MSX won't notice it. // TODO: What happens on non-transfer commands? if (!currentCommand) status &= 0x7F; transfer = true; } /** Gets the X coordinate of a border detected by SRCH (intended behaviour, * as documented in the V9938 technical data book). However, real VDP * simply returns the current value of the ASX 'temporary source X' counter, * regardless of the command that is being executed or was executed most * recently * @param time The moment in emulated time this get occurs. */ inline unsigned getBorderX(EmuTime::param time) { sync(time); return ASX; } /** Writes to a command register. * @param index The register [0..14] to write to. * @param value The new value for the specified register. * @param time The moment in emulated time this write occurs. */ void setCmdReg(byte index, byte value, EmuTime::param time); /** Read the content of a command register. This method is meant to * be used by the debugger, there is no strict guarantee that the * returned value is the correct value at _exactly_ this moment in * time (IOW this method does not sync the complete CmdEngine) * @param index The register [0..14] to read from. */ byte peekCmdReg(byte index); /** Informs the command engine of a VDP display mode change. * @param mode The new display mode. * @param time The moment in emulated time this change occurs. */ void updateDisplayMode(DisplayMode mode, EmuTime::param time); /** Interface for logical operations. */ template void serialize(Archive& ar, unsigned version); private: template